How to trigger error using Trap command
the ERR
trap is not to run code when the shell itself exits with a non-zero error code, but when any command run by that shell that is not part of a condition (like in if cmd...
, or cmd || ...
...) exits with a non-zero exit status (the same conditions as what causes set -e
to exit the shell).
If you want to run code upon exit of the shell with non-zero exit status, you should add a trap on EXIT
instead and check $?
there:
trap '[ "$?" -eq 0 ] || echo hi' EXIT
Note however that upon a trapped signal, both the signal trap and the EXIT trap would be run, so you may want to do it like:
unset killed_by
trap 'killed_by=INT;exit' INT
trap 'killed_by=TERM;exit' TERM
trap '
ret=$?
if [ -n "$killed_by" ]; then
echo >&2 "Ouch! Killed by $killed_by"
exit 1
elif [ "$ret" -ne 0 ]; then
echo >&2 "Died with error code $ret"
fi' EXIT
Or to use exit status like $((signum + 128))
upon signals:
for sig in INT TERM HUP; do
trap "exit $((128 + $(kill -l "$sig")))" "$sig"
done
trap '
ret=$?
[ "$ret" -eq 0 ] || echo >&2 "Bye: $ret"' EXIT
Note however that exiting normally upon SIGINT or SIGQUIT has potential annoying side effects when your parent process is a shell like bash
that implements the wait and cooperative exit handling of terminal interrupt. So, you may want to make sure to kill yourself with the same signal instead so as to report to your parent that you were indeed interrupted, and that it should consider exiting itself as well if it received a SIGINT/SIGQUIT.
unset killed_by
for sig in INT QUIT TERM HUP; do
trap "exit $((128 + $(kill -l "$sig"))); killed_by=$sig" "$sig"
done
trap '
ret=$?
[ "$ret" -eq 0 ] || echo >&2 "Bye: $ret"
if [ -n "$killed_by" ]; then
trap - "$killed_by" # reset handler
# ulimit -c 0 # possibly disable core dumps
kill -s "$killed_by" "$$"
else
exec "$ret"
fi' EXIT
If you want the ERR
trap to fire, just run a command with a non-zero exit status like false
or test
.
Use return, not exit, to set the status on exit from a function (if the function falls-through without a return, the status is that of the last statement executed.) If you substitute return
for exit
in the question's example, it will work as I think you intended: the trap will be triggered on the ERR pseudo-signal and 'hi' will be printed. For additional considerations, try this:
#!/bin/bash
func()
{
echo 'in func'
return 99
echo 'still in func'
}
trap 'echo "done"' EXIT
trap 'status=$?; echo "error status is $status"; trap - EXIT; exit $status' ERR
func
echo 'returned from func'
You can try various modifications, such as returning 0, commenting out the ERR trap, not canceling the EXIT trap within the ERR handler, not exiting from the ERR handler, or removing the return and putting false
as the last statement in func.