Why does bash set $? (exit status) to non-zero on Ctrl-C or Ctrl-Z?
Because 0
is the exit code for a normal exit state.
Intercepting an Interrupt or Break signal is not a usual exit state, nor is being suspended to the background. The non-zero exit codes tell you this is what is happening so that you can react accordingly in a script if the job it fires off is killed or suspended rather than exiting conventionally with a non-error state.
The interactive shell session, when you press ^C, which throws a SIGINT
signal (signal 2), aborts the current interactive command entry, which is a non-normal state for the command entry (i. e. the command prompt) to be in. This causes it to return status 130 (128+2), and give you a new prompt.
More details can be found at http://tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF.
When you press Ctrl+C on the command line, nothing exits, but the handler for SIGINT
(sigint_sighandler()
) sets the exit status to 130 (128 + 2, as DopeGhoti's answer explains) anyway:
if (interrupt_immediately)
{
interrupt_immediately = 0;
last_command_exit_value = 128 + sig;
throw_to_top_level ();
}
And in throw_to_top_level()
:
if (interrupt_state)
{
if (last_command_exit_value < 128)
last_command_exit_value = 128 + SIGINT;
print_newline = 1;
DELINTERRUPT;
}
When you press Ctrl+C to kill a background process, the shell observes that the process has died and also sets the exit status $?
to 128 plus the signal number.
When you press Ctrl+Z to suspend a background process, the shell observes that something has happened to the process: it hasn't died, but the information is reported through the same system call (wait
and friends). Here as well, the shell sets the exit status $?
to 128 plus the signal number, which is 148 (SIGTSTP = 20).