3

我正在编写一个 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()外壳的崩溃,传播到子外壳的崩溃,传播到外壳的崩溃。但是这些都没有崩溃,两个 NO 都被打印出来了(!?)

如果我取消注释该exit 1语句,则不再打印 NO,但 NOO 仍然是(!?)

我尝试set -e在 3 个 shell 中的每一个中显式使用(函数中的第一行,之后(的第一个语句,但没有变化。

注意:我需要在子shell 内执行管道(),因为这是对更复杂脚本的简化。没有()subshel​​l,一切都按预期工作,没有任何 NOfalseexit 1.

4

2 回答 2

0

这似乎是一个 bash 甚至 POSIX 错误:https ://groups.google.com/forum/?fromgroups=#!topic/gnu.bash.bug/NCK_0GmIv2M

于 2012-12-14T14:32:42.570 回答
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 !

于 2016-06-29T19:17:10.367 回答