为什么`if $(true); 然后...... fi`成功了?

Kei*_*son 22 bash ksh zsh sh

灵感来自这个问题:

当条件是命令替换时,如果命令不产生输出,if语句应该做什么?

注意:示例是if $(true); then ...,而不是if true ; then ...

例如,给定:

if $(true) ; then echo yes ; else echo no ; fi
Run Code Online (Sandbox Code Playgroud)

我认为$(true)应该用true命令的输出代替,这没什么.它应该等同于:

if "" ; then echo yes ; else echo no ; fi
Run Code Online (Sandbox Code Playgroud)

打印,no因为没有名称为空字符串的命令,或者这样:

if ; then echo yes ; else echo no ; fi
Run Code Online (Sandbox Code Playgroud)

这是一个语法错误.

但实验表明,如果命令不产生输出,则该if语句将其视为true或false,具体取决于命令的状态,而不是其输出.

这是一个演示行为的脚本:

#!/bin/bash

echo -n 'true:          ' ; if true          ; then echo yes ; else echo no ; fi
echo -n 'false:         ' ; if false         ; then echo yes ; else echo no ; fi
echo -n '$(echo true):  ' ; if $(echo true)  ; then echo yes ; else echo no ; fi
echo -n '$(echo false): ' ; if $(echo false) ; then echo yes ; else echo no ; fi
echo -n '$(true):       ' ; if $(true)       ; then echo yes ; else echo no ; fi
echo -n '$(false):      ' ; if $(false)      ; then echo yes ; else echo no ; fi
echo -n '"":            ' ; if ""            ; then echo yes ; else echo no ; fi
echo -n '(nothing):     ' ; if               ; then echo yes ; else echo no ; fi
Run Code Online (Sandbox Code Playgroud)

这是我得到的输出(Ubuntu 11.04,bash 4.2.8):

true:          yes
false:         no
$(echo true):  yes
$(echo false): no
$(true):       yes
$(false):      no
"":            ./foo.bash: line 9: : command not found
no
./foo.bash: line 10: syntax error near unexpected token `;'
./foo.bash: line 10: `echo -n '(nothing):     ' ; if               ; then echo yes ; else echo no ; fi'
Run Code Online (Sandbox Code Playgroud)

前四行符合我的预期; 这些$(true)$(false)线条令人惊讶.

进一步的实验(这里没有显示)表明如果命令$()生成输出之间,其退出状态不会影响行为if.

我看到类似的行为(但在某些情况下,不同的错误消息)bash,ksh,zsh,ash,和dash.

我在bash文档或POSIX"Shell Command Language"规范中没有看到任何解释.

(或许我错过了一些明显的东西.)

编辑:根据接受的答案,这是行为的另一个例子:

command='' ; if $command ; then echo yes ; else echo no ; fi
Run Code Online (Sandbox Code Playgroud)

或者,等效地:

command=   ; if $command ; then echo yes ; else echo no ; fi
Run Code Online (Sandbox Code Playgroud)

Wil*_*ell 15

请参阅语言规范的 2.9.1节.第一部分的最后一句是:

如果存在命令名称,则执行将继续执行命令搜索和执行中所述.如果没有命令名,但命令包含命令替换,则命令应以完成的最后一个命令替换的退出状态完成.否则,命令应以零退出状态完成.

$(true)扩大为空字符串.shell解析空字符串,发现没有给出命令,并遵循上述规则.


Aar*_*aid 6

可能需要考虑两个退出代码.首先,这是另外两个应该有用的实验:

# if $(echo true; false) ; then echo yes ; else echo no ; fi
yes
Run Code Online (Sandbox Code Playgroud)

所述内命令与由于故障退出false.但这是无关紧要的,因为命令的输出是非空的,因此执行输出("true")而其退出代码优先.

# if $(echo false; true) ; then echo yes ; else echo no ; fi
no
Run Code Online (Sandbox Code Playgroud)

同样,内部的命令行$( )成功,但输出为no,因为输出("false")优先.

当且仅当输出为空或仅为空格时,命令内部的命令的退出状态$( )是相关的.如果输出为空,则没有要执行的命令列表,因此看起来shell将回退到内部命令的退出状态.


Sie*_*geX 5

似乎正在发生的是bash保持最后执行的命令的退出状态

这可以解释为什么$(true)$(false)在一个不同的行为if测试.它们都产生空命令,这些命令不算作执行但它们有不同的退出代码.

只要你使用命令替换上输出了命令,$()试图执行一个输出作为命令和退出代码尝试,现在最新的一个用于if测试