How do I get the effect and usefulness of "set -e" inside a shell function?
Solution 1:
From documentation of set -e
:
When this 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
, orif
keyword, and is not a part of anAND
orOR
list, and is not a pipeline preceded by the!
reserved word, then the shell shall immediately exit.
In your case, false
is a part of a pipeline preceded by !
and a part of if
. So the solution is to rewrite your code so that it isn't.
In other words, there's nothing special about functions here. Try:
set -e
! { false; echo hi; }
Solution 2:
I eventually went with this, which apparently works. I tried the export method at first, but then found that I needed to export every global (constant) variable the script uses.
Disable set -e
, then run the function call inside a subshell that has set -e
enabled. Save the exit status of the subshell in a variable, re-enable set -e, then test the var.
f() { echo "a"; false; echo "Should NOT get HERE"; }
# Don't pipe the subshell into anything or we won't be able to see its exit status
set +e ; ( set -e; f ) ; err_status=$?
set -e
## cleaner syntax which POSIX sh doesn't support. Use bash/zsh/ksh/other fancy shells
if ((err_status)) ; then
echo "f returned false: $err_status"
fi
## POSIX-sh features only (e.g. dash, /bin/sh)
if test "$err_status" -ne 0 ; then
echo "f returned false: $err_status"
fi
echo "always print this"
You can't run f
as part of a pipeline, or as part of a &&
of ||
command list (except as the last command in the pipe or list), or as the condition in an if
or while
, or other contexts that ignore set -e
. This code also can't be in any of those contexts, so if you use this in a function, callers have to use the same subshell / save-exit-status trickery. This use of set -e
for semantics similar to throwing/catching exceptions is not really suitable for general use, given the limitations and hard-to-read syntax.
trap err_handler_function ERR
has the same limitations as set -e
, in that it won't fire for errors in contexts where set -e
won't exit on failed commands.
You might think the following would work, but it doesn't:
if ! ( set -e; f );then ##### doesn't work, f runs ignoring -e
echo "f returned false: $?"
fi
set -e
doesn't take effect inside the subshell because it remembers that it's inside the condition of an if
. I thought being a subshell would change that, but only being in a separate file and running a whole separate shell on it would work.