% Command exit status -- variable `$?` % Ian! D. Allen -- -- [www.idallen.com] % Winter 2018 - January to April 2018 - Updated 2018-11-05 17:37 EST - [Course Home Page] - [Course Outline] - [All Weeks] - [Plain Text] Command Exit Status -- 0 to 255 =============================== 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". No standard set of failure codes for all commands ================================================= 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. Checking Exit status using variable `$?` ======================================== On 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 Exit status of `grep` and `fgrep` ================================= Notable 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: - 0 ("success") if the search pattern was found in the input - 1 if the search pattern was not found in the input - 2 if there was an error in the pattern or error opening the file 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 Setting an exit status in a script ================================== 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. The `true` and `false` commands =============================== The 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. Using script logic to check exit status ======================================= *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. -- | Ian! D. Allen, BA, MMath - idallen@idallen.ca - Ottawa, Ontario, Canada | Home Page: http://idallen.com/ Contact Improv: http://contactimprov.ca/ | College professor (Free/Libre GNU+Linux) at: http://teaching.idallen.com/ | Defend digital freedom: http://eff.org/ and have fun: http://fools.ca/ [Plain Text] - plain text version of this page in [Pandoc Markdown] format [www.idallen.com]: http://www.idallen.com/ [Course Home Page]: .. [Course Outline]: course_outline.pdf [All Weeks]: indexcgi.cgi [Plain Text]: 720_exit_status.txt [Pandoc Markdown]: http://johnmacfarlane.net/pandoc/