我正在尝试运行这个脚本:
#!/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
之后的复合列表、以保留字开头的管道或while
until
if
elif
!
任何命令除最后一个之外的 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"
部分,向调用脚本返回错误。