为什么在条件中将打印的null与文字的null区别对待?

时间:2019-05-16 03:45:04

标签: bash shell

这两个命令具有相同的结果是有道理的:

$ 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 ""))的成功退出状态应用于整个条件,因此成功。

2 个答案:

答案 0 :(得分:2)

简短的回答:printf输出的空字符串在shell尝试对其执行命令查找之前,在应用于命令替换的分词过程中消失了。


shell读取其输入时,需要先评估并分析整个命令,然后再评估该命令。

在读取if之后,解析器将在执行任何评估之前致力于解析整个if语句。它期望的下一件事情是一个复合列表作为条件。复合列表不能以分号开头,因此解析器在看到if ;时会立即发出错误信号。

略过一点细节,足以说字符串$(print "") 确实被解析为复合列表。尚不需要评估,因此if $(print ""); then ...已成功解析。

解析完成后,现在外壳实际上必须对其进行评估。这样做的过程记录在bash手册页的“简单命令扩展”下,下面以其全文引用并突出显示了相关段落:

  

简单的命令扩展

     

执行简单命令时,shell从左到右执行以下扩展,分配和重定向。   是的。

     
      
  1. 解析器标记为变量分配(在命令名称之前的那些)和重定向的单词是   保存供以后处理-             

  2.   
  3. 不是变量分配或重定向的单词会被扩展。如果扩展后还剩下任何单词,则   第一个词被认为是             命令的名称和其余的单词就是参数。

  4.   
  5. 重定向是按照上文“重定向”中所述进行的。

  6.   
  7. 每个变量赋值中=后的文本均经过波浪线扩展,参数扩展,命令替换,   算术扩展             和引号删除,然后再分配给该变量。

         

    如果未产生命令名称,则变量分配会影响当前的Shell环境。否则,变量将添加到   的环境      执行的命令,不会影响当前的shell环境。如果有任何分配尝试将值分配给   一个只读变量,      发生错误,命令以非零状态退出。

         

    如果没有命令名称,将执行重定向,但不会影响当前的Shell环境。重定向错误导致   命令      以非零状态退出。

         

    如果扩展后还剩下命令名称,将按如下所述继续执行。 否则,命令退出。如果其中之一   扩展关系      完成命令替换后,命令的退出状态是上次执行命令替换的退出状态。   没有任何交流      强制替换,命令以零状态退出。

  8.   

因此,$(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 [