Single UNIX规范中用于描述的含义的set -e
确切语言是:
启用此选项时,如果一个简单命令由于“ Shell错误的后果”中列出的任何原因而失败,或者返回退出状态值> 0,并且不是[条件命令或否定命令],则Shell应该立即退出。
当这样的命令在子shell中发生时会发生什么,这是模棱两可的。从实际的角度来看,所有子外壳程序都可以做的是退出并向父外壳程序返回非零状态。父外壳程序是否将依次退出取决于此非零状态是否转换为在父外壳程序中失败的简单命令。
一个遇到问题的情况就是您遇到的一种情况:命令替换的返回状态为非零。由于此状态被忽略,因此不会导致父外壳退出。正如您已经发现的那样,一种考虑退出状态的方法是在简单的分配中使用命令替换:然后,赋值的退出状态就是赋值中最后一个命令替换的退出状态。
请注意,这仅在有单个命令替换的情况下才能按预期执行,因为仅考虑最后一个替换的状态。例如,以下命令是成功的(根据标准和我所见过的每个实现):
a=$(false)$(echo foo)
另一个需要注意的情况是显式子外壳:(somecommand)
。根据上面的解释,子外壳程序可能返回非零状态,但是由于这不是父外壳程序中的简单命令,因此父外壳程序应继续执行。实际上,我所知道的所有shell都会使父级此时返回。虽然这在许多情况下很有用,例如(cd /some/dir && somecommand)
使用括号将当前目录更改为本地操作,但如果set -e
在子shell中将其关闭,或者如果子shell以某种方式返回非零状态,则会违反规范。不会终止它,例如!
在true命令上使用。例如,所有ash,bash,pdksh,ksh93和zsh都退出而不显示foo
以下示例:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
然而,没有一个简单的命令set -e
在生效时失败!
第三个有问题的情况是非平凡管道中的元素。实际上,所有外壳程序都会忽略除最后一个管道元素之外的其他管道元素的故障,并表现出与最后一个管道元素有关的两种行为之一:
- ATT ksh和zsh会执行父外壳程序中管道的最后一个元素,因此照常进行操作:如果简单命令在管道的最后一个元素中失败,则执行该命令的外壳恰好是父外壳,退出。
- 如果管道的最后一个元素返回非零状态,则其他Shell通过退出来近似行为。
像以前一样,关闭set -e
或在管道的最后一个元素中使用否定符会导致其以不应该终止外壳的方式返回非零状态。然后,除ATT ksh和zsh外的其他shell将退出。
如果Bash的任何元素返回非零状态pipefail
,set -e
则Bash的选项将导致管道立即退出。
请注意,进一步复杂的是,set -e
除非它处于POSIX模式(set -o posix
或者POSIXLY_CORRECT
在bash启动时处于环境中),否则bash 在子shell中关闭。
所有这些表明,不幸的是,POSIX规范在指定-e
选项方面做得不好。幸运的是,现有的外壳在行为上基本上是一致的。