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

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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

Method 2

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.

Method 3

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.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x