The number passed to the _exit()
/exit_group()
system call (sometimes referred as the exit code to avoid the ambiguity with exit status which is also referring to an encoding of either the exit code or signal number and additional info depending on whether the process was killed or exited normally) is of type int
, so on Unix-like systems like Linux, typically a 32bit integer with values from -2147483648 (-231) to 2147483647 (231-1).
However, on all systems, when the parent process (or the child subreaper or init
if the parent died) uses the wait()
, waitpid()
, wait3()
, wait4()
system calls to retrieve it, only the lower 8 bits of it are available (values 0 to 255 (28-1)).
When using the waitid()
API (or a signal handler on SIGCHLD), on most systems (and as POSIX now more clearly requires in the 2016 edition of the standard (see _exit()
specification)), the full number is available (in the si_status
field of the returned structure). That is not the case on Linux yet though which also truncates the number to 8 bits with the waitid()
API, though that's likely to change in the future.
Generally, you'd want to only use values 0 (generally meaning success) to 125 only, as many shells use values above 128 in their $?
representation of the exit status to encode the signal number of a process being killed and 126 and 127 for special conditions.
You may want to use 126 to 255 on exit()
to mean the same thing as they do for the shell's $?
(like when a script does ret=$?; ...; exit "$ret"
). Using values outside 0 -> 255 is generally not useful. You'd generally only do that if you know the parent will use the waitid()
API on systems that don't truncate and you happen to have a need for the 32bit range of values. Note that if you do a exit(2048)
for instance, that will be seen as success by parents using the traditional wait*()
APIs.
More info at:
That Q&A should hopefully answer most of your other questions and clarify what is meant by exit status. I'll add a few more things:
A process cannot terminate unless it's killed or calls the _exit()
/exit_group()
system calls. When you return from main()
in C
, the libc calls that system call with the return value.
Most languages have a exit()
function that wraps that system call, and the value they take, if any is generally passed as is to the system call. (note that those generally do more things like the clean-up done by C's exit()
function that flushes the stdio buffers, runs the atexit()
hooks...)
That's the case of at least:
$ strace -e exit_group awk 'BEGIN{exit(1234)}'
exit_group(1234) = ?
$ strace -e exit_group mawk 'BEGIN{exit(1234)}'
exit_group(1234) = ?
$ strace -e exit_group busybox awk 'BEGIN{exit(1234)}'
exit_group(1234) = ?
$ echo | strace -e exit_group sed 'Q1234'
exit_group(1234) = ?
$ strace -e exit_group perl -e 'exit(1234)'
exit_group(1234) = ?
$ strace -e exit_group python -c 'exit(1234)'
exit_group(1234) = ?
$ strace -e exit_group expect -c 'exit 1234'
exit_group(1234) = ?
$ strace -e exit_group php -r 'exit(1234);'
exit_group(1234) = ?
$ strace -e exit_group zsh -c 'exit 1234'
exit_group(1234)
You occasionaly see some that complain when you use a value outside of 0-255:
$ echo 'm4exit(1234)' | strace -e exit_group m4
m4:stdin:1: exit status out of range: `1234'
exit_group(1) = ?
Some shells complain when you use a negative value:
$ strace -e exit_group dash -c 'exit -1234'
dash: 1: exit: Illegal number: -1234
exit_group(2) = ?
$ strace -e exit_group yash -c 'exit -- -1234'
exit: `-1234' is not a valid integer
exit_group(2) = ?
POSIX leaves the behaviour undefined if the value passed to the exit
special builtin is outside 0->255.
Some shells show some unexpected behaviours if you do:
bash
(and mksh
but not pdksh
on which it is based) takes upon itself to truncate the value to 8 bits:
$ strace -e exit_group bash -c 'exit 1234'
exit_group(210) = ?
So in those shells, if you do want to exit with a value outside of 0-255, you have to do something like:
exec zsh -c 'exit -- -12345'
exec perl -e 'exit(-12345)'
That is execute another command in the same process that can call the system call with the value you want.
as mentioned at that other Q&A, ksh93
has the weirdest behaviour for exit values from 257 to 256+max_signal_number where instead of calling exit_group()
, it kills itself with the corresponding signal¹.
$ ksh -c 'exit "$((256 + $(kill -l STOP)))"'
zsh: suspended (signal) ksh -c 'exit "$((256 + $(kill -l STOP)))"'
and otherwise truncates the number like bash
/mksh
.
¹ That's likely to change in the next version though. Now that the development of ksh93
has been taken over as a community effort outside of AT&T, that behaviour, even though encouraged somehow by POSIX, is being reverted
return
is, of course, a shell builtin. – Toby Speight Jan 24 '18 at 13:33bash
shell. Some other shells likezsh
can return any signed 32 bit value like forexit
. Some likerc
ores
can return data of any of the types they support (scalar or list). See the linked Q&A for details. – Stéphane Chazelas Jan 24 '18 at 15:36