I'm writing a bash script and I'd like it to crash on the first error. However, I can't get it to do this in a specific circumstance I simplified below:

#!/bin/bash
set -Exu
bad_command() {
  false
  #exit 1
  echo "NO!!"
}
(set -o pipefail; bad_command | cat ; echo "${PIPESTATUS[@]}; $?") || false
echo "NOO!!"

The expected behaviour would be a crash of the bad_command subshell, propagated to a crash of the () subshell, propagated to a crash of the outter shell. But none of those crash, and both NOs get printed(!?)

If I uncomment the exit 1 statement, then the NO is no longer printed, but NOO still is(!?)

I tried using set -e expicitly inside each of the 3 shells (first line in function, first statement after (, but there's no change.

Note: I need to execute the pipe inside the () subshell, because this is a simplification of a more elaborate script. Without the () subshell, everything works as expected, no NOs whatsoever with either false or exit 1.

有帮助吗?

解决方案

其他提示

After hitting the same problem, I have found a workaround. Actually 3 depending on what you want to achieve.

First a small rewrite of the OP example code since handling the exit code requires some extra work down the line:

#! /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

If it's only needed to have the errexit work, following is sufficient in calling bad_command. The trick is to launch bad_command in the background:

(bad_command) &
bc_pid=$!
if wait $bc_pid; then echo "NOO!!"; else echo "errexit worked: $?"; fi

If you want to work with the output as well (similar to abc=$(bad_command)), capture it in a temporary file as usual:

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

Finally, I found out in my testings that the wait command returned either 0 or 1 but not the actual exit code of bad_command (bash 4.3.42). This requires some more work:

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

For some strange reason, putting the if on one line as before got me exit code 0 in this case !

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top