为什么这个复合命令 {...} 与 || 一起使用时不会因错误而退出?

为什么这个复合命令 {...} 与 || 一起使用时不会因错误而退出?

我正在尝试运行这个脚本:

#!/bin/bash -e

{ 
    echo "Doing something"; 
    will_fail                 # like `false` 

    echo "Worked"; 
} || echo "Failed"

令我惊讶的是,will_fail失败了,但我在命令行上没有看到“失败”,而是“工作”。

为什么复合命令失败后没有错误退出will_fail

答案1

Failed不会被打印,因为复合命令的退出状态是 中执行的最后一个命令的退出状态{ ...; },即echo。成功echo,因此复合命令退出,退出状态为零。

以下将输出三个字符串:

{ echo "Do something"; echo "Worked"; false; } || echo "Failed"

POSIX 标准

除非另有说明,命令的退出状态应为该命令执行的最后一个简单命令的退出状态。


这里发生了几件事(摘要):

  • set -e积极地跑步。如果任何命令返回非零退出状态(广义而言),这将导致 shell 退出。但是,这不适用于此处,因为该will_fail命令是(复合命令,是||列表的一部分)的一部分(并且不是列表中的最后一个)。

    再次,从POSIX 标准(我的重点):

    当执行、、、 或保留字-e之后的复合列表、以保留字开头的管道或whileuntilifelif!任何命令除最后一个之外的 AND-OR 列表。

  • 列表中的最后一个简单命令||echo "Failed"。这决定了复合命令的整体退出状态。由于它执行成功(并且will_fail不会导致 shell 退出),因此状态将为零,这意味着 的另一端||不会被执行。

答案2

我认为介绍接受的答案(尽管我+1编辑了它的彻底性)是具有误导性的,并且它没有在存在的情况下解释这个确切的问题set -e。该行下方的其余部分包含答案。我决定写它,因为我自己就是来这里的,并且首先被误导了。

不会打印 Failed,因为复合命令的退出状态是 { ...; 中执行的最后一个命令的退出状态。 },这是回声。 echo 成功,因此复合命令退出,退出状态为零。

我认为首先应该传达的是,在这里,set -e由于位置{ ... }AND-OR 列表中的 的。如果删除该|| echo "Failed"部件,则复合命令{ ... }将在故障部件之后立即中断,甚至没有达到最后的命令{ ... }

比较

#!/bin/bash -e

{ 
    echo "Doing something"; 
    false
    echo "Worked"; 
}

之后不打印任何内容Doing something并返回错误退出,尽管(这就是为什么答案是错误的)是最后一个内部的复合{ ... }命令echo

尽管

#!/bin/bash -e

{ 
    echo "Doing something"; 
    false
    echo "Worked"; 
} || echo "Failed"

Worked之后打印Doing something,不调用echo "Failed"并返回 0 退出状态。

原因被埋在引文中(由接受的答案提供更深层次):set -e残疾人在属于 AND-OR 命令一部分的任何内容中。

对于尾随|| echo "Failed"{...}成为 AND-OR 命令的一部分,因此停止服从set -e。中间false的 不影响流程,继续进行echo "Worked",它返回 0 到 AND-OR,然后返回到调用脚本。

如果没有尾随|| echo "Failed"{ ... }本身就是一个正常的复合命令集,它不属于 POSIX 中提到的例外情况,并遵守set -e。当到达时执行停止false,并且执行流程甚至没有到达echo "Worked"部分,向调用脚本返回错误。

相关内容