I use set -e
inside (...)
on command line but it doesn't exit the subshell when false
command is executed.
Code 1:
( set -e; echo "$BASHPID: start"; false; echo "foobar"; date; ) &&
echo "$BASHPID: ok" || echo "$BASHPID: nope"
9136: start
foobar
Wed, Apr 29, 2015 7:14:24 PM
7292: ok
However if I use bash -c
for subshell creation then it behaves as I expect.
Code 2:
bash -c 'set -e; echo "$BASHPID: start"; false; echo "foobar"; date;' &&
echo "$BASHPID: ok" || echo "$BASHPID: nope"
7880: start
7292: nope
Interestingly if I remove &&
and ||
part after subshell then (...)
also behaves fine.
Code 3:
( set -e; echo "$BASHPID: start"; false; echo "foobar"; date; )
5940: start
So conclusions I am getting are:
(...)
behaves differently from bash -c
(...) && false
behaves differently from (...)
and changes behavior of subshell as well.Am I making some obvious mistake in interpreting this strange (at least for me) behavior?
The behavior of set -e
with &&
is intentional.
It explicitly doesn't trigger when the command is part of a &&
sequence.
From the POSIX spec:
-e
When this option is on, when any command fails (for any of the reasons listed in Consequences of Shell Errors or by returning an exit status greater than zero), the shell immediately shall exit with the following exceptions: The failure of any individual command in a multi-command pipeline shall not cause the shell to exit. Only the failure of the pipeline itself shall be considered.
The -e setting shall be ignored when executing the compound list following the while, until, if, or elif reserved word, a pipeline beginning with the ! reserved word, or any command of an AND-OR list other than the last.
If the exit status of a compound command other than a subshell command was the result of a failure while -e was being ignored, then -e shall not apply to this command.
This requirement applies to the shell environment and each subshell environment separately. For example, in:
set -e; (false; echo one) | cat; echo two
the false command causes the subshell to exit without executing echo one; however, echo two is executed because the exit status of the pipeline (false; echo one) | cat is zero.
I assume the difference here then is whether the shell knows that fact.
(...)
presumably does because the current shell executes it and bash -c
presumably doesn't because that's a fully external process (or something).
That last bit is speculation and to my mind the sub-shell behavior here is entirely unhelpful and makes set -e
unreliable (which is why many people suggest avoiding it).
Though it seems like this might also provide a sort of "escape hatch" for that reliability problem if an explicitly run shell doesn't have that issue with composition of scripts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With