"trap ... INT TERM EXIT" really necessary?

Yes, there is a difference.

This script will exit when you press Enter, or send it SIGINT or SIGTERM:

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

This script will exit when you press Enter:

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

* Tested in sh, Bash, and Zsh.   (no longer works in sh when you add a command for trap to run)


There's also what @Shawn said: Ash and Dash don't trap signals with EXIT.

So, to handle signals robustly, it's best to avoid trapping EXIT altogether, and use something like this:

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup

The POSIX spec doesn't say much about the conditions resulting in executing the EXIT trap, only about what its environment must look like when it is executed.

In Busybox's ash shell, your trap-exit test does not echo 'TRAP' before exiting due to either SIGINT or SIGTERM. I would suspect there are other shells in existance that may not work that way as well.

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 

Refining the last answer, because it has issues:

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

Points above:

INT and TERM handlers don't quit for me when I test - they handle the error then the shell returns to exiting (and this is not too surprising). So I ensure that the cleanup exits afterwards, and in the case of the signals always uses an error code (and in the other case of a normal exit, preserves the error code).

With bash, it seems that exiting in the INT handler also calls the EXIT handler, hence I untrap the exit handler and call it myself (which will work in any shell regardless of behaviour).

I trap exit because shell scripts can exit before they reach the bottom - syntax errors, set -e and a nonzero return, simply calling exit. You can't rely on a shellscript getting to the bottom.

SIGQUIT is Ctrl-\ if you've never tried it. Gets you a bonus coredump. So I think it's also worth trapping, even if it's a little obscure.

Past experience says if you (like me) always press Ctrl-C several times, you'll sometimes catch it half way through the cleanup part of your shell script, so this works but not always as perfectly as you'd like.