$?
Winter 2017 - January to April 2017 - Updated 2018-11-05 17:37 EST
When a Unix/Linux command (process) terminates it sets a numeric exit status (also called exit code or return value) between 0 and 255 that is available to the parent process that started (forked) the command.
Normally your shell doesn’t tell you the exit status of commands that it runs, but the shell puts the exit status of the previous command into the shell variable named $?
, and you can make the exit status visible using echo $?
right after you run the command:
$ date
Thu Nov 19 11:08:05 EST 2015
$ echo "The exit status was $?"
The exit status was 0
An exit status of 0 (zero) normally means “success” – the command worked.
The shell $?
variable is set after every command the shell runs, including after running echo
(which always succeeds). Note how this works:
$ fgrep 'nosuchthing' /etc/passwd
$ echo "The exit status was $?"
The exit status was 1
$ echo "The exit status was $?"
The exit status was 0
A non-zero exit status normally means something failed or went wrong:
$ rm nosuchfile
rm: cannot remove ‘nosuchfile’: No such file or directory
$ echo "The exit status was $?"
The exit status was 1
$ nosuchcommand
nosuchcommand: command not found
$ echo "The exit status was $?"
The exit status was 127
A non-zero exit status usually means “the command failed”.
The meaning of a particular non-zero exit code varies from command to command; there is no standard set of non-zero exit codes that apply across all commands. See the man pages for each command for details.
The only universal standard for command exit codes is that “a zero exit code means success”. Think of exit status as “there is only one way to succeed (only one zero); there are many ways to fail (many non-zero)”.
Some badly-written commands fail but still return zero. Sorry!
Programmers used to other programming languages may find that a command return code of zero meaning “success” and a non-zero exit status meaning “failure” backwards from what they are used to in evaluating arithmetic expressions, where zero means FALSE and anything non-zero means TRUE. Yes; this backwards. Think of command exit status as “there is only one way to succeed (only one zero); there are many ways to fail (many non-zero)”. Don’t get “running commands” confused with “evaluating Boolean expressions”. Shells run commands and check exit statuses; they don’t do math.
$?
IndexOn the command line or inside a shell script, after running a command we can use the value of the shell $?
variable immediately after a command runs to check the exit status of that command:
$ fgrep "no such text" /etc/passwd
$ echo "The exit status was $?"
The exit status was 1
$ echo "The exit status was $?"
The exit status was 0
The exit status is set after every command, including shell built-in commands. The second use of $?
above refers to the successful (zero) exit status of the preceding echo
command. If you need to use the exit status of a command more than once, save it in a variable:
$ fgrep "no such text" /etc/passwd
$ status=$?
$ echo "The exit status was $status"
The exit status was 1
$ echo "The exit status was $status"
The exit status was 1
grep
and fgrep
IndexNotable exceptions to this zero-is-success and non-zero-is-failure rule are the searching commands grep
and fgrep
that return different types of failure:
Examples of success and both types of failure:
$ fgrep "root:" /etc/passwd >/dev/null
$ echo $?
0
$ fgrep "no such text" /etc/passwd
$ echo $?
1
$ fgrep "anything" nosuchfile
fgrep: nosuchfile: No such file or directory
$ echo $?
2
A shell script is run by a shell process, and that shell process has its own exit status when it finishes reading your script. Normally, a shell script exits with the status of the last command run inside the script:
$ cat test.sh
#!/bin/sh -u
fgrep 'no such text' /etc/passwd
$ ./test.sh
$ echo "The exit status of the script is $?"
The exit status of the script is 1
You can exit a shell script and set the exit status of its shell process using an explicit exit
statement with a number argument between 0
and 255
:
$ cat test.sh
#!/bin/sh -u
exit 99
$ ./test.sh
$ echo "The exit status of the script is $?"
The exit status of the script is 99
An exit
statement inside a shell script causes the script to stop running and the shell process running it to terminate.
true
and false
commandsIndexThe command true
does nothing except return a good (zero) exit status.
The command false
does nothing except return a bad (non-zero) exit status.
$ true ; echo $?
0
$ false ; echo $?
1
These commands are often built in to your shell, but they are also separate executable programs with their own man
pages, and sometimes the commands even have options. Both commands usually ignore most arguments, but the built-in versions may not handle options exactly the same way as the external versions:
bash$ type true
true is a shell builtin
bash$ true --help # built-in version has no options
bash$ which true
/bin/true
bash$ /bin/true --help # external version has output from options
Usage: /bin/true [ignored command line arguments]
or: /bin/true OPTION
[... more help output ...]
These commands are never used interactively; they are most useful in shell scripts.
You need to understand shell script control flow for this section.
Scripts can use control flow statements to check the exit status of commands to know whether the commands worked and whether the script should continue processing or issue an error message and stop:
#!/bin/sh -u
fgrep "$1" /etc/passwd # look for argument in passwd
status=$? # save the status for later
if [ "$status" = 0 ] ; then
echo 1>&2 "$0: Success status $status - found the string '$1' in passwd"
exit "$status"
fi
if [ "$status" = 1 ] ; then
echo 1>&2 "$0: Not found status $status - did not find '$1' in passwd"
exit "$status"
fi
if [ "$status" = 2 ] ; then
echo 1>&2 "$0: File error status $status - unknown error with passwd"
exit "$status"
fi
The above script uses some conditional logic if
statements to test the exit status saved in the $status
variable. The script checks the saved exit status three times. It issues different messages on stderr depending on whether the saved exit status of the fgrep
command was 0
, 1
, or 2
. The script itself uses an exit
statement with a $status
number argument to cause the shell process to exit with exactly the same exit status as the fgrep
command.