#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
a=$(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
f() { :; }
f $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
还有其他情况吗?还是某种概括?
TL,DR:为了受益
set -e
,直接将命令替换的结果分配给变量(可选地在其周围加上额外的字符串)。不要将多个命令替换组合在一起或在命令参数中使用命令替换。问题不在于
inherit_errexit
. 它正在工作。问题是set -e
(不是特定于 bash 的限制:其他类似 sh 的 shell 也有同样的问题)。演示:运行第二个示例的这个变体。
请注意,
echo >&2 after cat
未执行。如果inherit_errexit
是关闭的,那将是。问题是
set -e
仅在简单情况下停止执行错误。如果命令替换返回失败状态,则不会停止执行包含替换的简单命令。它最多可能会设置简单命令的返回状态,这反过来可能会停止脚本的执行。“简单命令”由赋值、重定向和可选的可执行命令名称和参数组成。如果重定向失败,则返回状态为1。否则,如果有命令名,则简单命令的返回状态为可执行命令的返回状态。否则返回状态是最后一个命令替换的返回状态,如果没有,则返回 0。以下是一些简单命令的示例:true </no/such/file
→ 状态 1 由于重定向失败false </dev/null
→ 状态 1 从false
a=b
→ 状态 0,因为没有任何可能失败的部分a=$(exit 0) b=$(exit 1) c=$(exit 2)
→ 上次命令替换的状态 2a=$(exit 2) b=$(exit 1) c=$(exit 0)
→ 上次命令替换的状态 0true $(exit 0) $(exit 1) $(exit 2)
→ 状态 0 从true
再一次,在所有情况下,
set -e
仅当命令的状态为非零时才会停止脚本。嵌入式命令的状态不直接相关。因此,在您的第二个脚本
echo -n $(…)
中,状态为 0,来自echo
(除非echo
无法写入),无论命令替换内部发生什么。因此,即使set -e
处于活动状态,脚本也不会在这里停止。同样,在第三个脚本f $(…)
中,状态为 0 来自f
.