#!/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
还有其他案例吗?或者一些概括?
答案1
TL,DR:要从中受益set -e
,请将命令替换的结果直接分配给变量(可以选择在其周围添加额外的字符串)。不要将多个命令替换组合在一起或在命令参数中使用命令替换。
问题不在于inherit_errexit
.它正在发挥作用。问题是set -e
(这不是 bash 所特有的:其他类似 sh 的 shell 也有同样的问题)的局限性。
演示:运行第二个示例的变体。
$ cat b2.sh
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file; echo >&2 after cat)
echo survived
$ ./b2.sh
cat: no-such-file: No such file or directory
survived
请注意,echo >&2 after cat
没有执行。如果inherit_errexit
关闭的话就会这样。
问题是set -e
仅在简单情况下出现错误时才会停止执行。如果命令替换返回失败状态,则不会停止包含替换的简单命令的执行。它最多可能设置简单命令的返回状态,这可能反过来停止脚本的执行。 “简单命令”由赋值、重定向和可选的可执行命令名称和参数组成。如果重定向失败,则返回状态为1。否则,如果有命令名,则简单命令的返回状态为可执行命令的返回状态。否则返回状态是最后一个命令替换的返回状态,如果没有则返回0。以下是一些简单命令的示例:
true </no/such/file
→ 由于重定向失败而导致状态 1false </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
。