Keep exit codes when trapping SIGINT and similar?

Actually, interrupting bash's internal read seems to be a bit different to interrupting a command run by bash. Normally, when you enter trap, $? is set and you can preserve it and exit with the same value:

trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT

If your script is interrupted when executing a command like sleep or even a builtin like wait, you will see

130 SIGINT
130 EXIT

and the exit code is 130. However, for read -p, it seems $? is 0 (on my version of bash 4.3.42 anyway).


The handling of signals during read might be work in progress, according to the changes file in my release... (/usr/share/doc/bash/CHANGES)

changes between this version, bash-4.3-alpha, and the previous version, bash-4.2-release.

  1. New Features in Bash

    r. When in Posix mode, `read' is interruptible by a trapped signal. After running the trap handler, read returns 128+signal and throws away any partially-read input.


Any usual signal exit code will be available in $? upon entry to the trap handler:

sig_handler() {
    exit_status=$?  # Eg 130 for SIGINT, 128 + (2 == SIGINT)
    echo "Doing signal-specific up"
    exit "$exit_status"
}
trap sig_handler INT HUP TERM QUIT

If there is a separate EXIT trap, you can use the same methodology: immediately keep the exit status passed from the signal handler (if there is one) clean-up, then return the saved exit status.


All you need to do is change the EXIT handler inside your cleanup handler. Here's an example:

#!/bin/bash
cleanup() {
    echo trapped exit
    trap 'exit 0' EXIT
}
trap cleanup EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. '