这两个命令具有相同的结果是有道理的:
$ if 1; then printf "success\n"; else printf "failure\n"; fi
-bash: 1: command not found
failure
$ if $(printf 1); then printf "success\n"; else printf "failure\n"; fi
-bash: 1: command not found
failure
因为$(printf "1")
在1
中执行1
之前先打印if
。
尽管如此,我不明白为什么它们会产生不同的结果:
$ if ""; then printf "success\n"; else printf "failure\n"; fi
-bash: : command not found
failure
$ if ; then printf "success\n"; else printf "failure\n"; fi
-bash: syntax error near unexpected token `;'
$ if $(printf ""); then printf "success\n"; else printf "failure\n"; fi
success
为什么在第一个命令或第二个命令中缺少参数的情况下,将$(printf "")
输出的空字符串与显式编码为""
的空字符串区别对待?在最终命令中执行的是什么,发现成功了,为什么?
更新-确保我明白了!
因此,将@chepner's answer应用于上述脚本,我已经添加了解释(如果发现任何错误,请纠正我):
$ if 1; then printf "success\n"; else printf "failure\n"; fi
-bash: 1: command not found
failure
shell解析if 1;
,看到它应该是命令的内容,但实际上是数字1
,因此失败了“找不到命令”
$ if $(printf 1); then printf "success\n"; else printf "failure\n"; fi
-bash: 1: command not found
failure
shell解析if $(printf 1);
,看到它期望是命令$(printf 1)
,成功执行该命令并输出1。shell看到输出了某些内容(1
),然后给定在此上下文中的任何输出,Shell希望“某物”也是它应该能够执行的命令,但实际上是数字1,因此会失败“找不到命令”。
$ if ""; then printf "success\n"; else printf "failure\n"; fi
-bash: : command not found
failure
shell解析if "";
,看到它应该是命令的内容,但实际上是字符串“”,因此失败了“找不到命令”
$ if ; then printf "success\n"; else printf "failure\n"; fi
-bash: syntax error near unexpected token `;'
shell解析if ;
,未在该上下文中找到预期的命令,因此失败了“语法错误”
$ if $(printf ""); then printf "success\n"; else printf "failure\n"; fi
success
shell解析if $(printf "");
,看到它期望是命令$(printf "")
,成功执行了该命令,它没有输出shell接受的任何内容,因此没有新的命令要执行,因此将它确实执行的最后一条命令($(printf "")
)的成功退出状态应用于整个条件,因此成功。
答案 0 :(得分:2)
简短的回答:printf
输出的空字符串在shell尝试对其执行命令查找之前,在应用于命令替换的分词过程中消失了。
shell读取其输入时,需要先评估并分析整个命令,然后再评估该命令。
在读取if
之后,解析器将在执行任何评估之前致力于解析整个if
语句。它期望的下一件事情是一个复合列表作为条件。复合列表不能以分号开头,因此解析器在看到if ;
时会立即发出错误信号。
略过一点细节,足以说字符串$(print "")
确实被解析为复合列表。尚不需要评估,因此if $(print ""); then ...
已成功解析。
解析完成后,现在外壳实际上必须对其进行评估。这样做的过程记录在bash
手册页的“简单命令扩展”下,下面以其全文引用并突出显示了相关段落:
简单的命令扩展
执行简单命令时,shell从左到右执行以下扩展,分配和重定向。 是的。
解析器标记为变量分配(在命令名称之前的那些)和重定向的单词是 保存供以后处理-
不是变量分配或重定向的单词会被扩展。如果扩展后还剩下任何单词,则 第一个词被认为是 命令的名称和其余的单词就是参数。
重定向是按照上文“重定向”中所述进行的。
每个变量赋值中=后的文本均经过波浪线扩展,参数扩展,命令替换, 算术扩展 和引号删除,然后再分配给该变量。
如果未产生命令名称,则变量分配会影响当前的Shell环境。否则,变量将添加到 的环境 执行的命令,不会影响当前的shell环境。如果有任何分配尝试将值分配给 一个只读变量, 发生错误,命令以非零状态退出。
如果没有命令名称,将执行重定向,但不会影响当前的Shell环境。重定向错误导致 命令 以非零状态退出。
如果扩展后还剩下命令名称,将按如下所述继续执行。 否则,命令退出。如果其中之一 扩展关系 完成命令替换后,命令的退出状态是上次执行命令替换的退出状态。 没有任何交流 强制替换,命令以零状态退出。
因此,$(print "")
成功,但扩展为空单词列表。结果,if
语句的条件通过立即返回命令替换的退出状态来“执行”,导致采用了真正的分支。
答案 1 :(得分:1)
查看退出代码:
$ 1; echo $?
1: command not found
127
$ $(printf 1); echo $?
1: command not found
127
$ ""; echo $?
Command '' not found
127
$(printf ""); echo $?
0
您可能想要:
$ if [ 1 ]; then printf "success\n"; else printf "failure\n"; fi
success
$ if [ $(printf 1) ]; then printf "success\n"; else printf "failure\n"; fi
success
$ if [ "" ]; then printf "success\n"; else printf "failure\n"; fi
failure
$ if [ $(printf "") ]; then printf "success\n"; else printf "failure\n"; fi
failure
请注意之后和
[
之前的空格]
选中man [