我正在编写一个bash脚本,我希望它在第一个错误时崩溃。但是,我在下面简化的特定情况下无法做到这一点:
#!/bin/bash
set -Exu
bad_command() {
false
#exit 1
echo "NO!!"
}
(set -o pipefail; bad_command | cat ; echo "${PIPESTATUS[@]}; $?") || false
echo "NOO!!"
预期的行为是bad_command
子shell的崩溃,传播到()
子shell的崩溃,传播到outter shell的崩溃。但没有一个崩溃,两个NO都打印出来了(!?)
如果我取消注释exit 1
语句,则不再打印NO,但NOO仍然是(!?)
我尝试在3个shell中的每一个中明确使用set -e
(函数的第一行,(
之后的第一个语句,但是没有变化。
注意:我需要在()
子shell中执行管道,因为这是对更复杂脚本的简化。没有()
子shell,一切都按预期工作,对false
或exit 1
没有任何NO。
答案 0 :(得分:0)
这似乎是一个bash甚至POSIX错误:https://groups.google.com/forum/?fromgroups=#!topic/gnu.bash.bug/NCK_0GmIv2M
答案 1 :(得分:0)
遇到同样的问题后,我找到了解决方法。实际上3取决于你想要达到的目标。
首先对OP示例代码进行小的重写,因为处理退出代码需要一些额外的工作:
#! /bin/bash
set -eEu
bad_command_extra() {
return 42
}
bad_command() {
bad_command_extra
echo "NO!!"
}
if bad_command; then echo "NOO!!"; else echo "errexit worked: $?"; fi
如果只需要让errexit工作,则可以通过以下方式调用bad_command
。诀窍是在后台启动bad_command
:
(bad_command) &
bc_pid=$!
if wait $bc_pid; then echo "NOO!!"; else echo "errexit worked: $?"; fi
如果您也想使用输出(类似于abc=$(bad_command)
),请照常将其捕获到临时文件中:
tmp_out=$(mktemp)
tmp_err=$(mktemp)
(bad_command >$tmp_out 2>$tmp_err) &
bc_pid=$!
if wait $bc_pid; then echo "NOO!!"; else echo "errexit worked: $?"; fi
cat $tmp_out $tmp_err
rm -f $tmp_out $tmp_err
最后,我在测试中发现wait
命令返回0或1但不返回bad_command
的实际退出代码(bash 4.3.42)。这需要更多的工作:
tmp_out=$(mktemp)
tmp_err=$(mktemp)
tmp_exit=$(mktemp)
echo 0 > $tmp_exit
(
get_exit () {
echo $? > $tmp_exit
}
trap get_exit ERR
bad_command >$tmp_out 2>$tmp_err
) &
bc_pid=$!
bc_exit=$(cat $tmp_exit)
if wait $bc_pid
then echo "NOO!!"
else echo "errexit worked: $bc_exit"
fi
cat $tmp_out $tmp_err
rm -f $tmp_out $tmp_err $tmp_exit
出于某些奇怪的原因,将if
放在一行上就像以前一样让我退出代码0!