Bash 为什么在某种条件下,打印空值与文本空值的处理方式不同?

Bash 为什么在某种条件下,打印空值与文本空值的处理方式不同?,bash,shell,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”)在if中执行1之前正在打

具有相同结果的这两个命令都有意义:

$ 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”)
if
中执行
1
之前正在打印
1

尽管如此,我不明白为什么这些会产生不同的结果:

$ 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“”)
输出的空字符串与第一个命令中显式编码为
“”
的空字符串或第二个命令中缺少的参数区别对待?在最后的命令中执行的是什么,并且发现它成功了,为什么


更新-确保我得到它

因此,应用上述脚本,我添加了解释(如果我有任何错误,请纠正我):

shell解析
如果为1
,看到它期望是一个命令,但实际上是数字
1
,因此失败“command not found”

shell解析
if$(printf 1),看到它期望成为命令的内容,
$(printf 1)
,成功执行它,输出1。shell看到输出了某个命令(
1
),并且在此上下文中给定任何输出,shell希望“something”也是一个它应该能够执行的命令,但实际上是数字1,因此失败“command not found”

shell解析
if“”,看到它期望是命令的内容,但实际上是字符串“”,因此失败“未找到命令”

shell解析
if,找不到预期在该上下文中找到的命令,因此失败“语法错误”

shell解析
if$(printf“”)
,看到它期望成为命令的内容,
$(printf“”)
,成功执行该命令,该命令不会输出shell接受的任何内容,因此没有新命令要执行,因此应用了它运行的最后一个命令的成功退出状态(
$(printf“”)
)将该条件作为一个整体,从而成功。

查看退出代码:

$ 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[

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


当shell读取其输入时,它需要在计算命令之前读取并解析整个命令

在读取
if
之后,解析器将在执行任何计算之前解析整个
if
语句。接下来,它希望使用复合列表作为条件。但是,复合列表不能以分号开头,因此解析器在看到
if;
时会立即发出错误信号

略过一点细节,就可以说字符串
$(print“”)
是作为复合列表进行解析的。还不需要求值,因此
如果$(print“”);那么…
成功解析

解析完成后,现在shell实际上必须对其进行评估。执行过程记录在
bash
手册页的“简单命令扩展”下,完整引用如下,并突出显示相关段落:

简单命令扩展

当执行一个简单的命令时,shell将执行以下从左到右的扩展、分配和重定向 对

  • 解析器标记为变量赋值(命令名之前的赋值)和重定向的单词是 保存以供以后处理- 我很生气

  • 展开非变量赋值或重定向的单词。如果展开后仍有单词,则 第一个词被认为是 命令名和剩余的单词是参数

  • 重定向按照上述重定向下的说明执行

  • 在每个变量赋值中,=后面的文本将进行平铺展开、参数展开、命令替换, 算术展开, 并在分配给变量之前删除引号

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

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

    如果展开后留下命令名,执行过程如下所述。否则,命令退出。如果 扩展是不可能的- t如果执行命令替换,该命令的退出状态是上次执行的命令替换的退出状态。如果 没有com- 命令替换时,命令将以零状态退出


  • 因此,
    $(print“”)
    成功,但扩展到一个空单词列表。因此,
    if
    语句的条件“执行”通过立即返回命令替换的退出状态,导致执行真正的分支。

    这是解析器未能找到必要的语法结构和求值器找到要求值的空复合列表之间的区别。我目前找不到引用,但我认为POSIX指定空命令将被视为无效的成功。我添加了一个答案;我想我找到了所有重要的细节。对我来说,案例之间的差异$(prin
    $ 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
    
    $ 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