How do I get -e
/ errexit to work in bash functions, so that the first failed command* within a function causes the function to return with an error code (just as -e
works at top-level).
* not part of boolean expression, if/elif/while/etc etc etc
I ran the following test-script, and I expect any function containing f
in its name (i.e. a false
line in its source) to return error-code, but they don't if there's another command after. Even when I put the function body in a subshell with set -e
specified again, it just blindly steamrolls on through the function after a command fails instead of exiting the subshell/function with the nonzero status code.
Environments / interpreters tested:
I get the same result in all of these envs/shells, which is to be expected I guess unless one was buggy.
Arch:
bash test.sh
(bash 5.1)zsh test.sh
(zsh 5.8)sh test.sh
(just a symlink to bash)
Alpine:
docker run -v /test.sh:/test.sh alpine:latest sh /test.sh
(BusyBox 1.34)
Ubuntu:
docker run -v /test.sh:/test.sh ubuntu:21.04 sh /test.sh
docker run -v /test.sh:/test.sh ubuntu:21.04 bash /test.sh
(bash 5.1)docker run -v /test.sh:/test.sh ubuntu:21.04 dash /test.sh
(bash 5.1)
Test script
set -e
t() {
true
}
f() {
false
}
tf() {
true
false
}
ft() {
false
true
}
et() {
set -e
true
}
ef() {
set -e
false
}
etf() {
set -e
true
false
}
eft() {
set -e
false
true
}
st() {( set -e
true
)}
sf() {( set -e
false
)}
stf() {( set -e
true
false
)}
sft() {( set -e
false
true
)}
for test in t f tf ft _ et ef etf eft _ st sf stf sft; do
if [ "$test" = '_' ]; then
echo ""
elif "$test"; then
echo "$test: pass"
else
echo "$test: fail"
fi
done
Output on my machine
t: pass
f: fail
tf: fail
ft: pass
et: pass
ef: fail
etf: fail
eft: pass
st: pass
sf: fail
stf: fail
sft: pass
Desired output
Without significantly changing source in functions themselves, i.e. not adding an if/then or || return
to every line in the function.
t: pass
f: fail
tf: fail
ft: fail
et: pass
ef: fail
etf: fail
eft: fail
st: pass
sf: fail
stf: fail
sft: fail
Or at least pass/fail/fail/fail in one group, so I can use that approach for writing robust functions.
With accepted solution
With the accepted solution, the first four tests give the desired result. The other two groups don't, but are irrelevant since those were my own attempts to work around the issue. The root cause is that the "don't errexit the script when if/while-condition fails" behaviour propagates into any functions called in the condition, rather than just applying to the end-result.