Shell scripts: Perils of “set -e” for error handling
By David Röthlisberger. Comments welcome at david@rothlis.net.
Published 12 May 2013. Last updated 7 Dec 2013. This article is Creative Commons licensed.
I used to be a fan of “set -e” in shell scripts. It causes the script to exit immediately if an error occurs, except where you explicitly handle the error with:
some_command_that_might_fail || true # to ignore error, or:
some_command_that_might_fail || fallback_to_this_other_command
Recently I’ve had a lot of trouble with “set -e”. For example this prints “a”, as you’d expect:
set -e
myfun() { printf a; false; printf b; }
myfun
printf c
…because the “set -e” terminates the whole script immediately after running the “false” command.
But this script prints “a-b-True”, where some people (myself included) might have expected it to print “a-False”:
set -e
myfun() { printf a-; false; printf b-; }
if myfun; then
printf True
else
printf False
fi
The “set -e” is ignored completely. Putting another “set -e” inside the definition of myfun doesn’t make any difference.
The posix standard explains this behaviour:
When this [set -e] option is on, if a simple command fails for any of the reasons listed in Consequences of Shell Errors or returns an exit status value >0, and is not part of the compound list following a while, until, or if keyword, and is not a part of an AND or OR list, and is not a pipeline preceded by the ! reserved word, then the shell shall immediately exit.
Bash experts seem to agree: “never rely on set -e” (gnu.bash.bug, BashFAQ). The recommendation is to do your own error checking by stringing together a series of commands with “&&” like this:
mkdir abc &&
cd abc &&
do_something_else &&
last_thing ||
{ echo error >&2; exit 1; }
A few important notes: You don’t need a trailing backslash. You don’t indent the following line. You must stick to 80 chars per line so that everyone can see the “&&” or “||” at the end. If you need to mix ANDs and ORs, group with {
these braces }
which don’t spawn a sub-shell.