考虑一种简单的调试风格,其中$debug
可以设置为true
或false
根据某些命令行标志:
$debug && echo "Something strange just happened" >&2\n
Run Code Online (Sandbox Code Playgroud)\n我知道有更好的方法来防止值设置,例如${debug:-false}
甚至是全功能的logMessage()
-type 函数,但让我们暂时搁置它。
假设仍然未设置或为空的糟糕情况$debug
。我预计$debug && echo \xe2\x80\xa6
这样的空值$debug
将被解析并计算为&& echo \xe2\x80\xa6
,这将触发随后的语法错误。但相反,它似乎被评估为类似的东西: && echo \xe2\x80\xa6
,最终执行echo
.
我已经用bash
、ksh
和进行了测试dash
,并且场景保持一致。
我的理解是这两行被解析为等价的,但显然它们不是:
\nunset debug; $debug && echo stuff\nunset debug; && echo stuff\n
Run Code Online (Sandbox Code Playgroud)\n为什么代码会表现得好像未设置的变量在逻辑上为真,而不是因诸如 之类的错误而失败syntax error near unexpected token `&&'
?
ilk*_*chu 14
但扩展完成后不会检查语法。
$foo
是一个语法上有效的简单命令,无论变量的值是什么以及扩展后生成的字段数如何。它是扩展之前的一个 shell“单词”,这就足够了。
等于$foo && whatever
类似于&& whatever
期望 shell 也解析其他语法(关键字、引号等)的扩展结果。那还没有完成。
因此:
foo="then if"
$foo
Run Code Online (Sandbox Code Playgroud)
不是语法错误,而是尝试运行名为then
( 它可能不存在并且您会收到错误,但它可能存在,并且“命令未找到”错误与尝试的语法错误不同的then if
常规命令自行运行。) 。
和:
foo=
$foo
Run Code Online (Sandbox Code Playgroud)
不是语法错误,而是尝试运行...好吧,它不是因为没有“命令名称”,虽然这是一种特殊情况,但它不是错误。请参见2.9.1 简单命令:
当需要执行给定的简单命令时,以下扩展、赋值和重定向都将从命令文本的开头到结尾执行:
[分配、重定向]
不是变量赋值或重定向的单词应该被扩展。如果扩展后仍有任何字段,则第一个字段应被视为命令名称,其余字段是命令的参数。 [...]
注意“如果”。
没有命令名的结果是变量赋值会影响当前执行环境,如果也没有命令替换,则退出状态为零。
因此,如果$foo
为空或未设置:
$foo
Run Code Online (Sandbox Code Playgroud)
只是不执行任何操作并将退出状态设置为零,并且
bar=asdf $foo
Run Code Online (Sandbox Code Playgroud)
设置$bar
为asdf
并将退出状态设置为零。与 just 相同bar=asdf
,但如果$foo
did 扩展为命令名称,则分配将仅适用于该命令。
Wil*_*ell 10
根据 Shell 命令语言规范,第2.9.1 节简单命令,在执行所有重定向、全局/参数扩展等之后:
如果有命令名称,则应按照命令搜索和执行中的描述继续执行。如果没有命令名称,但该命令包含命令替换,则该命令应以上次执行的命令替换的退出状态完成。否则,该命令应以零退出状态完成。
你属于“否则”的情况。
一个简单的命令由赋值、重定向和参数组成(第一个参数用于派生要运行的命令)。
全部都是可选的。如果没有参数,只要可以执行所有重定向并且重定向和分配目标中的最后运行命令替换(或用于生成空参数列表的命令)成功,该命令就会成功,尽管 IIRC 未指定哪个重定向和分配首先完成。
所有这些都成功了:
a=whatever
:没有命令,没有重定向,只有赋值$(true)
:没有命令,没有重定向,没有分配,成功的命令替换会导致空列表。a=$(false) b=$(true)
。只有赋值,最后一个命令替换成功。> /dev/null
:无命令,重定向成功> "$(echo /dev/null)"
:无命令,命令替换和重定向成功empty=; a=foo $empty > /dev/null
:赋值和重定向,以及导致空列表的扩展,因此没有参数/命令。empty=; $empty
:与上面相同,没有赋值或重定向。这些失败了:
false
a=$(false)
$(false)
> /etc/passwd/foo
< /dev/null"$(false)"
未指定:
$(true) > /dev/null$(false)
a=$(false) > /dev/null$(true)