Bashset-e没有立即退出管道失败
set -eo pipefail
commandThatFails || exitFunction
exitFunction
所以这个脚本运行 exitMethod 两次......我认为set -e在任何非零退出代码上立即退出并set -o pipefail确保在管道期间任何失败是最终退出状态代码而不是最近的命令?
因此我想:
- 命令失败
- 执行 exitFunction
- set -o pipefail 在第一个命令失败时返回非零退出代码
- set -e 检测到非零退出代码并立即退出
在文档中它指出:
如果失败的命令是紧跟在 while 或 until 关键字之后的命令列表的一部分、if 语句中测试的一部分、在 && 或 || 中执行的任何命令的一部分,则 shell 不会退出 列表除了最后一个 && 或 || 之后的命令,管道中除最后一个之外的任何命令,或者如果命令的返回状态正在用 ! 反转。如果除子 shell 之外的复合命令由于命令在 -e 被忽略时失败而返回非零状态,则 shell 不会退出。如果设置了 ERR 上的陷阱,则会在 shell 退出之前执行。
我认为 exitfunction 是command following the final ||so 因此会被计数并立即退出。
我可以通过以下方式解决问题:
commandThatFails || { exitFunction; exit 1; }
但这似乎不是更优雅的处理方式,任何想法都值得赞赏!
回答
||是一个流量控制算子,而不是一个管道组件。pipefail对它没有影响。
如果set -e导致流控制操作符退出,那么您将永远无法else运行脚本的分支并使其处于活动状态;这将是完全无用的。
出于这个原因,&&并||抑制set -e其左侧的行为,就像if condition; then success; else fail; fi抑制 的行为一样condition。
一般的做法是让你的exitFunction 实际调用exit,所以你可以写:
die() {
rc=$?
(( $# )) && printf '%sn' "$*" >&2
exit "$(( rc == 0 ? 1 : rc ))"
}
commandThatFails || die "commandThatFails failed"
...但是如果你想先调用别的东西,是的,你需要使用分组,就像你在问题中所做的那样。