了解 Bash 短路

Understanding Bash short-circuiting

首先我不是 Bash 专业人士。几个月前我发现,如果我同时使用 &&|| 短路运算符和花括号,那么如果第一个语句以真实值退出,如果最后一个语句为真块退出非零,那么失败块也将被执行。像这样:

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

将输出:

OK
NG

所以,我为此寻找了最简单的解决方案,并想出了这个:

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

我知道,省略内置的冒号很容易,但这是解决方法的正确方法吗?

这个其实很常见Bash pitfall不是错误。

returnNumber 0 的计算结果为真,因此第二个块(由逻辑和 && 连接)也被计算以确保 first && second 的结果仍然为真。
第二个块输出 OK 但计算结果为 false,因此现在 first && second 的结果为 false。这意味着第三部分(由逻辑或 || 连接)也必须计算,导致 NG 也被显示。


与其依赖 &&||,您应该使用 if 语句:

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

tl;dr:y 可以 return 非零退出状态时,切勿使用 x && y || z

先生Llama 已经正确回答了这个问题,这只是为了快速参考不同 "combinations" 会发生什么。 cmd0 是具有零退出状态的 "command" 而 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]