首页 > 解决方案 > Bash set -e 没有立即退出 pipefail

问题描述

set -eo pipefail

commandThatFails || exitFunction

exitFunction

所以这个脚本运行 exitMethod 两次......我认为set -e在任何非零退出代码上立即退出,并set -o pipefail确保在管道期间任何失败都是最终退出状态代码而不是最近的命令?

因此我想:

  1. 命令失败
  2. 执行退出函数
  3. set -o pipefail 在第一个命令失败时返回非零退出代码
  4. set -e 检测到非零退出代码并立即退出

在文档中它指出:

如果失败的命令是紧跟在 while 或 until 关键字之后的命令列表的一部分、if 语句中测试的一部分、在 && 或 || 中执行的任何命令的一部分,则 shell 不会退出 列出除了最后一个 && 或 || 之后的命令,管道中除最后一个之外的任何命令,或者命令的返回状态是否用 ! 反转。如果子 shell 以外的复合命令由于在忽略 -e 时命令失败而返回非零状态,则 shell 不会退出。如果设置了 ERR 陷阱,则会在 shell 退出之前执行。

我认为 exitfunction 是command following the final ||这样,因此会被计算并立即退出。

我可以通过以下方式解决问题:

commandThatFails || { exitFunction; exit 1; }

但这似乎不是更优雅的处理方式,任何想法都值得赞赏!

标签: bashshellexit-code

解决方案


||是流控制操作符,而不是管道组件。pipefail对它没有影响。

如果set -e导致流控制操作员退出,那么您将永远无法else让脚本的分支在其处于活动状态时运行;这将是完全没用的。

出于这个原因,&&并为他们的左侧||抑制行为,就像为 抑制该行为一样。set -eif condition; then success; else fail; ficondition


一般做法是进行exitFunction 实际调用exit,因此您可以编写:

die() {
  rc=$?
  (( $# )) && printf '%s\n' "$*" >&2
  exit "$(( rc == 0 ? 1 : rc ))"
}

commandThatFails || die "commandThatFails failed"

...但是如果您想先调用其他内容,是的,您需要使用分组,就像您在问题中所做的那样。


推荐阅读