There are recommendations to use the following options to make Bash fail fast:
set -o errexit
set -o nounset
set -o pipefail
These options however do not work as expected for Bash functions, piped via ||
.
E.g. in script
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
my_function() {
test -z "$1"
echo "this SHOULD NOT be printed"
}
my_function 1 || echo "???" # 1
my_function 1 # 2
echo "this will not be printed"
Line # 2
will cause the script to terminate with code 1 without any output. This is what I expect.
Line # 1
confuses me actually: my_function
will successfully be completed, printing "this SHOULD NOT be printed" and returning code 0, thus the "???" will not be printed.
How can I make Bash to process my_function
on line # 1
the same fail fast way, as on line # 2
?
There are also better recommendations not to use set -e
/errexit
:
Clearly we don't want to abort when the conditional (in
if [ -d /foo ]; then
), returns non-zero. [...] The implementors decided to make a bunch of special rules, like "commands that are part of an if test are immune", or "commands in a pipeline, other than the last one, are immune".These rules are extremely convoluted, and they still fail to catch even some remarkably simple cases. Even worse, the rules change from one Bash version to another, as Bash attempts to track the extremely slippery POSIX definition of this "feature". When a SubShell is involved, it gets worse still -- the behavior changes depending on whether Bash is invoked in POSIX mode.
If the script were to stop on a non-zero return, foo || bar
would also be useless because bar
would never run (the script would instead exit). Therefore, one of these convoluted rules is that non-zero returns in commands on the left-hand side of &&
or ||
will not cause the script to exit. This is the effect you're seeing.
There is no way to make set -e
work properly and there is no automatic replacement. You can not make it work the way you want without writing manual error handling.
Looking at the bash manual, here is what is says for the trap
command:
trap [-lp] [arg] [sigspec …]
If a sigspec is ERR, the command arg is executed whenever a pipeline (which may consist of a single simple command), a list, or a compound command returns a non-zero exit status, subject to the following conditions. The ERR trap is not executed if the failed command is part of the command list immediately following an until or while keyword, part of the test following the if or elif reserved words, part of a command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return status is being inverted using !. These are the same conditions obeyed by the errexit (-e) option.
Look at the last sentence especially. That explains it!
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