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 EXITthe cleanup code is executed twice whenSIGTERMorSIGINTis received.