4

From bash manual

The exit status of an executed command is the value returned by the waitpid system call or equivalent function. Exit statuses fall between 0 and 255, though, as explained below, the shell may use values above 125 specially. Exit statuses from shell builtins and compound commands are also limited to this range. Under certain circumstances, the shell will use special values to indicate specific failure modes.

  1. What determines the exit status of an executed command,

    • the command itself, i.e. does the implementation of the command determines the exit status, i.e. is determination of the exit status of an executed command an inherent property of the command, just like parsing the command line arguments to an execution of a command is inherent to the command, or
    • the shell process which executed the command?
      The waitpid system call mentioned above seems to me that the exit status of an executed command is implemented by the shell process, and the command itself has nothing to do with the exit status of an execution of itself.
  2. In a bash process, when can we get the exit status of an executed command,

    • only when the bash process is interactive, or
    • regardless of whether the bash process is interactive or non-interactive? When a bash process is non-interactive, how can we retrieve the exit status of a command executed in the bash process?
9

The command itself can provide the exit status, via the argument to an exit() system call. The shell (or other program, how about system() in Perl or PHP?) can pick up a child process' exit status via wait() or waitpid() system calls. The Unix/Linux/*BSD kernels all arrange to deliver SIGCHLD to the parent process when a child process changes status.

But there are other circumstances. A process terminated via SIGKILL doesn't have a chance to exit. A process that dereferences an invalid address won't have a chance to call exit() unless it has a SIGSEGV signal handler installed. In those cases, the kernel calculates an exit status that includes a bit that means "killed by a signal", and what signal caused the process to get killed. That kernel-calculated exit status is delivered to the killed process' parent process.

  • 2
    system() is executed using a shell. The "native" ways are things like execve. – user23013 Dec 10 '16 at 5:00
5

The code sets the exit status

$ perl -e 'END { $? = 42 }'; echo $?
42
$ 

except when it does not

$ perl -e 'sleep 999; END { $? = 42 }'
^C
$ echo $?
130
$ echo $((130-128))
2
$ kill -l | head -2
 1    HUP Hangup                        17   STOP Stopped (signal)         
 2    INT Interrupt                     18   TSTP Stopped
$ 

or does

$ perl -e '$SIG{INT}=sub{exit 7};sleep 999'                   
^C$ echo $?
7
$ 

or does not should someone get frisky with SIGKILL, etc.

Otherwise, the bash documentation is misleading in that waitpid sets a 16 bit status word, but bash passes along only a subset of that in the shell variable $?. This can lead to ignorance and confusion should a shell user attempt to deal with the results of waitpid as if it were an 8 bit value (it is not).

$ false; echo $?
1
$ perl -E 'system("false"); say $?'
256
$ perl -E 'system("false"); say $? >> 8'
1
$ 
2

A process determines its own exit code. When it terminates, it provides the exit code or, if none is provided, it is presumed to be zero (non-error).

With both interactive and non-interactive shells, the exit code of the previous command is obtainable through the value of $?.

  • If a bash process is not interactive, the shell process can't respond to your stdin input after executing a command, and then how can you type $? and get its value? Note that I am not talking about wrapping the command in a script and then writing $? to a file in the same script – Tim Dec 9 '16 at 18:36
  • 3
    If you do for example (exit 5) from an interactive shell; the subshell is non-interactive. $? still catches the exit code from the non-interactive shell's final command. $? is also available within a non-interactive shell: ( /bin/false; echo $? ) for instance. – DopeGhoti Dec 9 '16 at 18:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.