继承_errexit什么时候不起作用?

继承_errexit什么时候不起作用?
#!/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→ 由于重定向失败而导致状态 1
  • false </dev/null→ 状态 1 来自false
  • a=b→ 状态 0,因为没有任何可能发生故障的部件
  • a=$(exit 0) b=$(exit 1) c=$(exit 2)→ 上次命令替换的状态 2
  • a=$(exit 2) b=$(exit 1) c=$(exit 0)→ 上次命令替换后的状态 0
  • true $(exit 0) $(exit 1) $(exit 2)→ 状态 0 来自true

再次强调,在所有情况下,set -e只有当命令的状态非零时才会停止脚本。嵌入命令的状态不直接相关。

因此,在第二个脚本中,echo -n $(…)状态为 0,来自echo(除非echo无法写入),无论命令替换中发生什么。因此,即使脚本set -e处于活动状态,也不会在此停止。同样,在第三个脚本中,f $(…)的状态为 0 f

相关内容