理解Bash短路运算

3

首先,我不是Bash专家。几个月前我发现,如果将&&||短路运算符与花括号连用,那么如果第一个语句返回真值,并且true块中的最后一条语句返回非零值,则失败块也会被执行。就像这样:

returnNumber 0 && {
    echo 'OK'
    returnNumber 1
} || {
    echo 'NG'
}

将输出:

OK
NG

所以,我寻找了最简单的解决方案,并想出了以下方法:
returnNumber 0 && {
    echo 'OK'
    returnNumber 1
    :
} || {
    echo 'NG'
}

我知道,省略冒号内置函数很容易,但这是一个合适的解决方法吗?

不要把使用特殊语法当成一种娱乐方式。当你只写 if-then-else-fi 时,大多数人都能理解。 尝试考虑有效的例外情况。我仍然不确定我的回答 http://stackoverflow.com/a/28478184/3220113 :它是有效的,但很难阅读。 - Walter A
在这个上下文中,花括号是什么样的语法? - von spotz
2个回答

19

这实际上是一个非常常见的Bash陷阱,它并不是一个bug。

returnNumber 0返回true,所以第二个块(由逻辑运算符&&连接)也被执行了,以确保first && second的结果仍然为true。
第二个块输出OK,但其结果为false,现在first && second的结果为false。这意味着第三部分(由逻辑或||连接)也必须被执行,导致NG也被显示。


不要依赖于 &&||,你应该使用 if 语句:

if returnNumber 0; then
    echo 'OK'
    returnNumber 1
else
    echo 'NG'
fi

简而言之:y 可以返回非零退出状态时,永远不要使用 x && y || z


参见:https://dev59.com/hG7Xa4cB1Zd3GeqPpGGB#60812984 - 使用短路求值来评估 false 条件,将在到达 && command 之前退出。改用 [[ ! condition ]] || command - Noam Manos

0

Llama先生已经正确回答了问题,这只是为了快速参考不同“组合”发生的情况。 cmd0 是一个具有零退出状态的“命令”,而cmd1则是非零。

cmd0() { echo -n "[$@-0]"; return 0; }
cmd1() { echo -n "[$@-1]"; return 1; }
second() { echo "[second]"; }

doit() { echo "case: $@"; eval "$@"; echo; }

doit 'cmd0 start && cmd0 first && second'
doit 'cmd0 start && cmd0 first || second'
doit 'cmd0 start || cmd0 first && second'
doit 'cmd0 start || cmd0 first || second'

doit 'cmd0 start && cmd1 first && second'
doit 'cmd0 start && cmd1 first || second'
doit 'cmd0 start || cmd1 first && second'
doit 'cmd0 start || cmd1 first || second'

doit 'cmd1 start && cmd0 first && second'
doit 'cmd1 start && cmd0 first || second'
doit 'cmd1 start || cmd0 first && second'
doit 'cmd1 start || cmd0 first || second'

doit 'cmd1 start && cmd1 first && second'
doit 'cmd1 start && cmd1 first || second'
doit 'cmd1 start || cmd1 first && second'
doit 'cmd1 start || cmd1 first || second'

产生:

case: cmd0 start && cmd0 first && second
[start-0][first-0][second]

case: cmd0 start && cmd0 first || second
[start-0][first-0]

case: cmd0 start || cmd0 first && second
[start-0][second]

case: cmd0 start || cmd0 first || second
[start-0]

case: cmd0 start && cmd1 first && second
[start-0][first-1]

case: cmd0 start && cmd1 first || second
[start-0][first-1][second]

case: cmd0 start || cmd1 first && second
[start-0][second]

case: cmd0 start || cmd1 first || second
[start-0]

case: cmd1 start && cmd0 first && second
[start-1]

case: cmd1 start && cmd0 first || second
[start-1][second]

case: cmd1 start || cmd0 first && second
[start-1][first-0][second]

case: cmd1 start || cmd0 first || second
[start-1][first-0]

case: cmd1 start && cmd1 first && second
[start-1]

case: cmd1 start && cmd1 first || second
[start-1][second]

case: cmd1 start || cmd1 first && second
[start-1][first-1]

case: cmd1 start || cmd1 first || second
[start-1][first-1][second]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接