Why does bash set $? (exit status) to non-zero on Ctrl-C or Ctrl-Z?

From the bash manual, on the $? variable:

$? Expands to the exit status of the most recently executed
foreground pipeline.

I wonder why bash updates the $? variable on pressing Ctrl-C or Ctrl-Z:

$ echo $?
$ ^C
$ echo $?
$ sleep 10
[1]+  Stopped                 sleep 10
$ echo $?
Asked By: Eugene Yarmash


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.

Answered By: DopeGhoti

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;

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).

Answered By: muru

The standard says that the value in $? in case on a received signal is > 128 but the standard does not say how this has to be done.

The Bourne Shell sets $? in case of a singal, it uses 128 + signal number

ksh93 uses 256 + signal number

Note that only signal numbers 1, 2, 3, 6, 9, 14, and 15 are portable.

In other words: SIGTSTP and SIGSTOP use non-portable numbers.

For this reason, the Bourne Shell recently introduced a new portable system, see http://schillix.sourceforge.net/man/man1/bosh.1.html see the ${.sh.termsig} variable.

Answered By: schily