11

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

3 Answers 3

10

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

4
  • Cool. Any clue how come it's set to 148 on Ctrl-Z ? Commented Dec 28, 2015 at 21:34
  • @eugeney Ctrl-Z is SIGTSTP, which is signal 20. (See man 7 signal.) Commented Dec 28, 2015 at 21:39
  • Does the process actually exit in this case, or this is again handled by bash somehow? Commented Dec 28, 2015 at 21:46
  • 1
    No, the process doesn't exit (that would be a bug), it is stopped. Again, in this case, the exit status is set by throw_to_level() (see the lines immediately after those which I quoted). Commented Dec 28, 2015 at 21:55
9

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.

4
  • I agree, but what process does exit when I just press Ctrl-C in bash? Commented Dec 28, 2015 at 21:08
  • 1
    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. Commented Dec 28, 2015 at 21:15
  • 2
    That comment ^ should be part of the answer. Commented Feb 19, 2016 at 19:30
  • I shall make it so. Commented Feb 20, 2016 at 0:00
1

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.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.