Frage

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.

War es hilfreich?

Lösung

Andere Tipps

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 !

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top