Bash Exit Status – Why Does Bash Set $? to Non-Zero on Ctrl-C or Ctrl-Z?

background-processbashcommand lineexit-statussignals

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 $?
0
$ ^C
$ echo $?
130
$ sleep 10
^Z
[1]+  Stopped                 sleep 10
$ echo $?
148

Best Answer

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