I am working on a Bash script with several functions. I would like to exit the script if any commands return an nonzero exit code, unless in a context where that code is being explicitly handled (such as in an if
condition or before a ||
alternative). To make this easier, I have removed all use of sub-shells from my script, and enabled every error handling option I could find.
Unfortunately, I'm still encountering a common pattern where errors are being suppressed.
If a command in a function returns a nonzero exit code, but is not the last command in the function, and the function is being called as the first part of an &&
expression, the exit code is ignored.
#!/bin/bash
trap 'echo "Error occurred." && exit' ERR
set -o errexit; # -e
set -o errtrace; # -E
set -o pipefail;
first-step-fails() {
# These should be redundant, but are repeated to be certain and clear.
trap 'echo "Error occurred." && exit' ERR
set -o errexit; # -e
set -o errtrace; # -E
set -o pipefail;
false; # exit code 1 (failure)
true; # exit code 0 (success)
echo "A is executed.";
}
first-step-fails && echo "B is executed.";
A is executed.
B is executed.
I didn't expect either echo
s to be executed following the call to false
.
If the function isn't called in a &&
expression, the error is trapped:
#!/bin/bash
trap 'echo "Error occurred." && exit' ERR
set -o errexit; # -e
set -o errtrace; # -E
set -o pipefail;
first-step-fails() {
# These should be redundant, but are repeated to be certain and clear.
trap 'echo "Error occurred." && exit' ERR
set -o errexit; # -e
set -o errtrace; # -E
set -o pipefail;
false; # exit code 1 (failure)
true; # exit code 0 (success)
echo "A is executed.";
}
first-step-fails ### && echo "B is executed.";
Error occurred.
If the failure is the last step in the function, the error isn't trapped but of course the function passes on its nonzero exit status and suppresses the outer echo
.
#!/bin/bash
trap 'echo "Error occurred." && exit' ERR
set -o errexit; # -e
set -o errtrace; # -E
set -o pipefail;
first-step-fails() {
# These should be redundant, but are repeated to be certain and clear.
trap 'echo "Error occurred." && exit' ERR
set -o errexit; # -e
set -o errtrace; # -E
set -o pipefail;
false; # exit code 1 (failure)
### true; # exit code 0 (success)
### echo "A is executed.";
}
first-step-fails && echo "B is executed.";
(no output)
How can I have my bash script exit when there's an nonzero exit status returned from a command called inside a function that's invoked on the left-hand-side of an &&
expression?
I have been testing on macOS (built-in GNU Bash 4.4.23) but need a solution that also works on Alpine Linux (packaged GNU Bash 4.4.19).