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

时间:2012-01-22 23:59:47

标签: bash ksh zsh sh

灵感来自this question

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

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

例如,给定:

if $(true) ; then echo yes ; else echo no ; fi

我认为$(true)应该被true命令的输出所取代,这是什么都没有。它应该等同于:

if "" ; then echo yes ; else echo no ; fi

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

if ; then echo yes ; else echo no ; fi

这是语法错误。

但是实验表明,如果命令没有产生输出,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

这是我得到的输出(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'

前四行符合我的预期; $(true)$(false)行令人惊讶。

进一步的实验(此处未显示)表示如果$()之间的命令产生输出,则其退出状态不会影响if的行为。

我在bashkshzshashdash中看到了类似的行为(但在某些情况下会出现不同的错误消息)。

我在bash文档或POSIX“Shell Command Language”规范中没有看到任何解释这一点。

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

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

command='' ; if $command ; then echo yes ; else echo no ; fi

或等同于:

command=   ; if $command ; then echo yes ; else echo no ; fi

4 个答案:

答案 0 :(得分:15)

language spec的第2.9.1节。第一部分的最后一句是:

  

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

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

答案 1 :(得分:6)

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

# if $(echo true; false) ; then echo yes ; else echo no ; fi
yes

由于false内部命令退出失败。但这是无关紧要的,因为命令的输出是非空的,因此执行输出(“true”)而其退出代码优先。

# if $(echo false; true) ; then echo yes ; else echo no ; fi
no

同样,$( )内的命令行成功,但输出为no,因为输出(“false”)优先。

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

答案 2 :(得分:5)

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

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

只要对具有输出的命令使用命令替换,$()会尝试将该输出作为命令执行,而的退出代码尝试现在是最新使用的对于if测试

答案 3 :(得分:3)

  

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

输出无关紧要。重要的是退出代码

   if list; then list; [ elif list; then list; ] ... [ else
   list; ] fi
          The if list is executed.  If its exit status is zero,
          the then list is executed.  Otherwise, each elif list
          is executed in turn, and if its exit status is zero,
          the corresponding then list is executed and the
          command completes.  Otherwise, the else list is
          executed, if present.  The exit status is the exit
          status of the last command executed, or zero if no
          condition tested true.

如果您用其他内容替换$(echo true)$(echo false),您可能会看到正在发生的事情:

$ if $(echo false) ; then echo yes ; else echo no ; fi
no
$ if $(echo command-does-not-exist) ; then echo yes ; else echo no ; fi
command-does-not-exist: command not found
no
$ 

$(..)运行命令,然后if 执行结果(在这种情况下,仅truefalse或{{1 }})。空does-not-exist启动子shell,该子shell成功运行至完成并返回退出代码$()

0

亚伦提出了一些有趣的观点:

$ if $() ; then echo yes ; else echo no ; fi
yes

看来,在执行通过$ if $(echo true; false) ; then echo yes ; else echo no ; fi yes $ if $(echo 'true ; false') ; then echo yes ; else echo no ; fi yes $ if true ; false ; then echo yes ; else echo no ; fi no $ if $(echo false ; true) ; then echo yes ; else echo no ; fi no $ if $(echo 'false ; true') ; then echo yes ; else echo no ; fi no $ if false ; true ; then echo yes ; else echo no ; fi yes $ 子shell构造的命令时,第一个命令的退出状态才是最重要的。