It depends on what you're trying to achieve, and which shells you're targeting. For bash
it's probably okay to just use EXIT
. But not all shells invoke the EXIT
handler on SIGINT
/SIGTERM
.
For them you can try to set one handler for several signals (trap '...' INT EXIT
), but then it may be invoked several times:
$ bash -c 'trap "echo trap" INT EXIT; sleep 3' & pid=$!; sleep 1; kill -INT $pid; wait
[1] 276923
trap
trap
[1]+ Done bash -c 'trap "echo trap" INT EXIT; sleep 3'
So either you write it with that in mind, or you can try to forward everything to the EXIT
handler:
$ bash -c 'trap "exit 123" INT; trap "echo EXIT \$?" EXIT; sleep 3' & pid=$!; sleep 1; kill -INT $pid; wait
[1] 286229
EXIT 123
[1]+ Exit 123 bash -c 'trap "exit 123" INT; trap "echo EXIT \$?" EXIT; sleep 3'
But if you set up a handler for SIGINT
, you generally want it to kill the script with SIGINT
:
a.sh
:
trap 'exit 123' INT
trap 'echo EXIT $?; trap - INT; kill -INT $$' EXIT
sleep 3
$ bash h.sh & pid=$!; sleep 1; kill -INT $pid; wait $pid
[1] 236263
EXIT 123
[1]+ Interrupt bash h.sh
And under Debian < 10 (dash < 0.5.10
) the signal that killed the script (if any) is not passed.
The solution I came up with:
set -eu
cleanup() {
echo "cleanup ($1)"
trap - INT TERM EXIT # avoid reexecuting handlers
if [ "$1" = 130 ]; then
kill -INT $$
elif [ "$1" = 143 ]; then
kill -TERM $$
else
exit "$1"
fi
}
trap 'cleanup 130' INT
trap 'cleanup 143' TERM
trap 'cleanup $?' EXIT
if [ "${1-}" = fail ]; then
no-such-command
fi
sleep 3
$ bash f.sh; echo $?
cleanup (0)
0
$ bash f.sh fail; echo $?
f.sh: line 20: no-such-command: command not found
cleanup (127)
127
$ bash f.sh & pid=$!; sleep 1; kill -INT $pid; wait $pid
[1] 282422
cleanup (130)
[1]+ Interrupt bash f.sh
$ bash f.sh & pid=$!; sleep 1; kill -TERM $pid; wait $pid
[1] 282458
cleanup (143)
[1]+ Terminated bash f.sh
Tested in:
bash
: 5.1.8
- dash:
0.5.10
, 0.5.8
, 0.5.7
- Alpine Linux 3.14 (
busybox
)
INT TERM EXIT
the cleanup code is executed twice whenSIGTERM
orSIGINT
is received.