【问题标题】:Bash set -e not exiting immediately with pipefail [duplicate]Bash set -e 没有立即退出 pipefail [重复]
【发布时间】:2021-10-10 13:22:42
【问题描述】:
set -eo pipefail

commandThatFails || exitFunction

exitFunction

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

所以我想:

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

在文档中指出:

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

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

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

commandThatFails || { exitFunction; exit 1; }

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

【问题讨论】:

  • BashFAQ #105set -e 的行为非常不直观且不可移植(即使在不同版本的 bash 之间也是如此)。它使代码更难审查,并且更有可能在 shell 版本之间表现不同;一般不推荐使用。
  • in-ulm.de/~mascheck/various/set-e 是另一个参考,展示了 shell 可以通过多少种不同方式实现 set -e
  • 这里的另一个误解是foo | bar || baz | qux 是一个管道。这是两个; foo | bar 是一个,baz | qux 是另一个。

标签: bash shell exit-code


【解决方案1】:

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

如果set -e 导致流控制操作员退出,那么您的脚本的else 分支永远不会在它处于活动状态的情况下运行;完全没用。

因此,&&|| 抑制 set -e 左侧的行为,就像 if condition; then success; else fail; fi 抑制 condition 的行为一样。


一般的做法是让你的exitFunction实际上调用exit,所以你可以这样写:

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

commandThatFails || die "commandThatFails failed"

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

【讨论】:

  • 太棒了!没有对定义有所了解。现在更有意义了。干杯!
猜你喜欢
  • 2021-12-02
  • 1970-01-01
  • 2020-04-17
  • 2018-06-13
  • 2013-05-16
  • 1970-01-01
  • 1970-01-01
  • 2019-10-18
  • 2018-06-13
相关资源
最近更新 更多