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
.
Part of your problem is that you have the >> trap.log
outside the (quoted) command arg,
so all you’re getting in the trap.log
file is the output from the trap
command itself
– which is nothing.
I’m not sure what you mean by saying “TRAPPED & READY” when your script is terminating,
but it looks like what you mean is
trap 'rm -f filename; echo "message" >> trap.log' sigspec …
And I agree with Karlo: if you are
“just killing the servers which are being used by the script,”
then the script is quite probably exiting (rather than being killed by a signal)
and you should use the EXIT
(or, equivalently, 0
) sigspec
(possibly in addition to 1
, 2
, and 15
).
P.S. You don’t need a semicolon (;
) at the end of the trap
command arg.
Best Answer
Actually it did print, but you have to wait 100 seconds to see the result.
You said:
If you check
ps aux
, you will get something similar to:Your main script process of /bin/bash, PID 42624 still waiting for sleep 100 finish. Even main process received signal 1, it have to wait
sleep 100
finished first, after that is its turn to perform its task, i.e.echo "cleaning up!"
.You can make sleep process become background process. In this case, script process can perform
echo "cleaning up!"
without waiting thesleep process
, if and only if script process doesn't exit yet.We can proved the conceptual(i.e. script is waiting
sleep
before handle signal) via this script:Run this script by
./test.sh > output
as usual, then useps aux
to figure out the/bin/bash
PID is 23311, then dokill -1 23311
. In the same timecat output
to know the stage, i.e. when the script go intosleep 20
after echo 1, sendkill -1 23311
, now wait for 2 come out, you will notice "cleaning up" has been print together before 2. Finally is the turn ofecho 3
and the script exit.The experiment above proved that script receive the signal SIGHUP and perform the signal handler after all previous foreground process is done and to its turn, without waiting the background process.
The story become interesting, with your original script, what if you do
kill -1 PID_of_sleep_100
?It will exit all processes without print, because the sleep process doesn't return SIGHUP to script process.
So there's a solution to your task, you can do
kill -1 PPID
(script process) to acknowledge SIGHUP to script process, thenkill -1 PID
(sleep process). Now script process will do SIGHUP handler before exit, happy hacking :)Not all signals is same, e.g. SIGKILL is not allow to trap. If you kill -9 the script process, then sleep process will still running and its parent PID will become 1 (check by
ps -ef
).[UPDATE]:
Normally
kill -N script_PID
will kill directly the script if the signal N doesn't trap in your script. But be careful about SIGINT,kill -2 script_PID
will not directly kill even though your script doesn't trap it. Your script will wait your child process(e.g. sleep 10) done. Now assume you do multiple kill in script_PID, i.e.kill -2 script_PID
ANDkill -user-defined_trap_N script_PID
, then: