When a process is killed with a handle-able signal like SIGINT
or SIGTERM
but it does not handle the signal, what will be the exit code of the process?
What about for unhandle-able signals like SIGKILL
?
From what I can tell, killing a process with SIGINT
likely results in exit code 130
, but would that vary by kernel or shell implementation?
$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130
I'm not sure how I would test the other signals…
$ ./myScript &
$ killall myScript
$ echo $?
0 # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0 # same problem
Best Answer
Processes can call the
_exit()
system call (on Linux, see alsoexit_group()
) with an integer argument to report an exit code to their parent. Though it's an integer, only the 8 least significant bits are available to the parent (exception to that is when usingwaitid()
or handler on SIGCHLD in the parent to retrieve that code, though not on Linux).The parent will typically do a
wait()
orwaitpid()
to get the status of their child as an integer (thoughwaitid()
with somewhat different semantics can be used as well).On Linux and most Unices, if the process terminated normally, bits 8 to 15 of that status number will contain the exit code as passed to
exit()
. If not, then the 7 least significant bits (0 to 6) will contain the signal number and bit 7 will be set if a core was dumped.perl
's$?
for instance contains that number as set bywaitpid()
:Bourne-like shells also make the exit status of the last run command in their own
$?
variable. However, it does not contain directly the number returned bywaitpid()
, but a transformation on it, and it's different between shells.What's common between all shells is that
$?
contains the lowest 8 bits of the exit code (the number passed toexit()
) if the process terminated normally.Where it differs is when the process is terminated by a signal. In all cases, and that's required by POSIX, the number will be greater than 128. POSIX doesn't specify what the value may be. In practice though, in all Bourne-like shells that I know, the lowest 7 bits of
$?
will contain the signal number. But, wheren
is the signal number,in ash, zsh, pdksh, bash, the Bourne shell,
$?
is128 + n
. What that means is that in those shells, if you get a$?
of129
, you don't know whether it's because the process exited withexit(129)
or whether it was killed by the signal1
(HUP
on most systems). But the rationale is that shells, when they do exit themselves, by default return the exit status of the last exited command. By making sure$?
is never greater than 255, that allows to have a consistent exit status:ksh93
,$?
is256 + n
. That means that from a value of$?
you can differentiate between a killed and non-killed process. Newer versions ofksh
, upon exit, if$?
was greater than 255, kills itself with the same signal in order to be able to report the same exit status to its parent. While that sounds like a good idea, that means thatksh
will generate an extra core dump (potentially overwriting the other one) if the process was killed by a core generating signal:Where you could even say there's a bug is that
ksh93
kills itself even if$?
comes from areturn 257
done by a function:yash
.yash
offers a compromise. It returns256 + 128 + n
. That means we can also differentiate between a killed process and one that terminated properly. And upon exiting, it will report128 + n
without having to suicide itself and the side effects it can have.To get the signal from the value of
$?
, the portable way is to usekill -l
:(for portability, you should never use signal numbers, only signal names)
On the non-Bourne fronts:
csh
/tcsh
andfish
same as the Bourne shell except that the status is in$status
instead of$?
(note thatzsh
also sets$status
for compatibility withcsh
(in addition to$?
)).rc
: the exit status is in$status
as well, but when killed by a signal, that variable contains the name of the signal (likesigterm
orsigill+core
if a core was generated) instead of a number, which is yet another proof of the good design of that shell.es
. the exit status is not a variable. If you care for it, you run the command as:which will return a number or
sigterm
orsigsegv+core
like inrc
.Maybe for completeness, we should mention
zsh
's$pipestatus
andbash
's$PIPESTATUS
arrays that contain the exit status of the components of the last pipeline.And also for completeness, when it comes to shell functions and sourced files, by default functions return with the exit status of the last command run, but can also set a return status explicitly with the
return
builtin. And we see some differences here:bash
andmksh
(since R41, a regression^Wchange apparently introduced intentionally) will truncate the number (positive or negative) to 8 bits. So for instancereturn 1234
will set$?
to210
,return -- -1
will set$?
to 255.zsh
andpdksh
(and derivatives other thanmksh
) allow any signed 32 bit decimal integer (-231 to 231-1) (and truncate the number to 32bits).ash
andyash
allow any positive integer from 0 to 231-1 and return an error for any number out of that.ksh93
forreturn 0
toreturn 320
set$?
as is, but for anything else, truncate to 8 bits. Beware as already mentioned that returning a number between 256 and 320 could causeksh
to kill itself upon exit.rc
andes
allow returning anything even lists.Also note that some shells also use special values of
$?
/$status
to report some error conditions that are not the exit status of a process, like127
or126
for command not found or not executable (or syntax error in a sourced file)...