Bash command line exit codes demystified
If you’ve ever wondered what an exit code is or why it’s a 0, 1, 2, or even 255, you’re in the right place.
Great Linux resources
When you execute a command or run a script, you receive an exit code. An exit code is a system response that reports success, an error, or another condition that provides a clue about what caused an unexpected result from your command or script. Yet, you might never know about the code, because an exit code doesn’t reveal itself unless someone asks it to do so. Programmers use exit codes to help debug their code.
Note: You’ll often see exit code referred to as exit status or even as exit status codes. The terms are used interchangeably except in documentation. Hopefully my use of the two terms will be clear to you.
I’m not a programmer. It’s hard for me to admit that, but it’s true. I’ve studied BASIC, FORTRAN, and a few other languages both formally and informally, and I have to say that I am definitely not a programmer. Oh sure, I can script and program a little in PHP, Perl, Bash, and even PowerShell (yes, I’m also a Windows administrator), but I could never make a living at programming because I’m too slow at writing code and trial-and-error isn’t an efficient debugging strategy. It’s sad, really, but I’m competent enough at copying and adapting found code that I can accomplish my required tasks. And yet, I also use exit codes to figure out where my problems are and why things are going wrong.
Exit codes are useful to an extent, but they can also be vague. For example, an exit code of 1 is a generic bucket for miscellaneous errors and isn’t helpful at all. In this article, I explain the handful of reserved error codes, how they can occur, and how to use them to figure out what your problem is. A reserved error code is one that’s used by Bash and you shouldn’t create your own error codes that conflict with them.
Enough backstory. It’s time to look at examples of what generates error codes/statuses.
Extracting the elusive exit code
To display the exit code for the last command you ran on the command line, use the following command:
The displayed response contains no pomp or circumstance. It’s simply a number. You might also receive a shell error message from Bash further describing the error, but the exit code and the shell error together should help you discover what went wrong.
Exit status 0
An exit status of 0 is the best possible scenario, generally speaking. It tells you that your latest command or script executed successfully. Success is relative because the exit code only informs you that the script or command executed fine, but the exit code doesn’t tell you whether the information from it has any value. Examples will better illustrate what I’m describing.
For one example, list files in your home directory. I have nothing in my home directory other than hidden files and directories, so nothing to see here, but the exit code doesn’t care about anything but the success of the command’s execution:
The exit code of 0 means that the ls command ran without issue. Although, again, the information from exit code provides no real value to me.
Now, execute the ls command on the /etc directory and then display the exit code:
$ ls /etc **Many files** $ echo $? 0
You can see that any successful execution results in an exit code of 0, including something that’s totally wrong, such as issuing the cat command on a binary executable file like the ls command:
$ cat /usr/bin/ls **A lot of screen gibberish and beeping** $ echo $? 0
Exit status 1
Using the above example but adding in the long listing and recursive options ( -lR ), you receive a new exit code of 1:
$ ls -lR /etc **A lengthy list of files** $ echo $? 1
Although the command’s output looks as though everything went well, if you scroll up you will see several «Permission denied» errors in the listing. These errors result in an exit status of 1, which is described as «impermissible operations.» Although you might expect that a «Permission denied» error leads to an exit status of 1, you’d be wrong, as you will see in the next section.
Dividing by zero, however, gives you an exit status of 1. You also receive an error from the shell to let you know that the operation you’re performing is «impermissible:»
$ let a=1 $ let b=0 $ let c=a/b -bash: let: c=a/b: division by 0 (error token is "b") $ echo $? 1
Without a shell error, an exit status of 1 isn’t very helpful, as you can see from the first example. In the second example, you know why you received the error because Bash tells you with a shell error message. In general, when you receive an exit status of 1, look for the impermissible operations (Permission denied messages) mixed with your successes (such as listing all the files under /etc , as in the first example in this section).
Exit status 2
As stated above, a shell warning of «Permission denied» results in an exit status of 2 rather than 1. To prove this to yourself, try listing files in /root :
$ ls /root ls: cannot open directory '/root': Permission denied $ echo $? 2
Exit status 2 appears when there’s a permissions problem or a missing keyword in a command or script. A missing keyword example is forgetting to add a done in a script’s do loop. The best method for script debugging with this exit status is to issue your command in an interactive shell to see the errors you receive. This method generally reveals where the problem is.
Permissions problems are a little less difficult to decipher and debug than other types of problems. The only thing «Permission denied» means is that your command or script is attempting to violate a permission restriction.
Exit status 126
Exit status 126 is an interesting permissions error code. The easiest way to demonstrate when this code appears is to create a script file and forget to give that file execute permission. Here’s the result:
$ ./blah.sh -bash: ./blah.sh: Permission denied $ echo $? 126
This permission problem is not one of access, but one of setting, as in mode. To get rid of this error and receive an exit status of 0 instead, issue chmod +x blah.sh .
Note: You will receive an exit status of 0 even if the executable file has no contents. As stated earlier, «success» is open to interpretation.
I receiving an exit status of 126. This code actually tells me what’s wrong, unlike the more vague codes.
Exit status 127
Exit status 127 tells you that one of two things has happened: Either the command doesn’t exist, or the command isn’t in your path ( $PATH ). This code also appears if you attempt to execute a command that is in your current working directory. For example, the script above that you gave execute permission is in your current directory, but you attempt to run the script without specifying where it is:
$ blah.sh -bash: blah.sh: command not found $ echo $? 127
If this result occurs in a script, try adding the explicit path to the problem executable or script. Spelling also counts when specifying an executable or a script.
Exit status 128
Exit status 128 is the response received when an out-of-range exit code is used in programming. From my experience, exit status 128 is not possible to produce. I have tried multiple actions for an example, and I can’t make it happen. However, I can produce an exit status 128-adjacent code. If your exit code exceeds 256, the exit status returned is your exit code subtracted by 256. This result sounds odd and can actually create an incorrect exit status. Check the examples to see for yourself.
Using an exit code of 261, create an exit status of 5:
$ bash $ exit 261 exit $ echo $? 5
To produce an errant exit status of 0:
$ bash $ exit 256 exit $ echo $? 0
If you use 257 as the exit code, your exit status is 1, and so on. If the exit code is a negative number, the resulting exit status is that number subtracted from 256. So, if your exit code is 20, then the exit status is 236.
Troubling, isn’t it? The solution, to me, is to avoid using exit codes that are reserved and out-of-range. The proper range is 0-255.
Exit status 130
If you’re running a program or script and press Ctrl-C to stop it, your exit status is 130. This status is easy to demonstrate. Issue ls -lR / and then immediately press Ctrl-C:
$ ls -lR / **Lots of files scrolling by** ^C $ echo $? 130
There’s not much else to say about this one. It’s not a very useful exit status, but here it is for your reference.
Exit status 255
This final reserved exit status is easy to produce but difficult to interpret. The documentation that I’ve found states that you receive exit status 255 if you use an exit code that’s out of the range 0-255.
I’ve found that this status can also be produced in other ways. Here’s one example:
$ ip **Usage info for the ip command** $ echo $? 255
Some independent authorities say that 255 is a general failure error code. I can neither confirm nor deny that statement. I only know that I can produce exit status 255 by running particular commands with no options.
Wrapping up
There you have it: An overview of the reserved exit status numbers, their meanings, and how to generate them. My personal advice is to always check permissions and paths for anything you run, especially in a script, instead of relying on exit status codes. My debug method is that when a command doesn’t work correctly in a script, I run the command individually in an interactive shell. This method works much better than trying fancy tactics with breaks and exits. I go this route because (most of the time) my errors are permissions related, so I’ve been trained to start there.
Have fun checking your statuses, and now it’s time for me to exit.
[ Want to try out Red Hat Enterprise Linux? Download it now for free. ]
Appendix D. Exit Codes With Special Meanings
According to the above table, exit codes 1 — 2, 126 — 165, and 255 [1] have special meanings, and should therefore be avoided for user-specified exit parameters. Ending a script with exit 127 would certainly cause confusion when troubleshooting (is the error code a «command not found» or a user-defined one?). However, many scripts use an exit 1 as a general bailout upon error. Since exit code 1 signifies so many possible errors, this probably would not be helpful in debugging.
There has been an attempt to systematize exit status numbers (see /usr/include/sysexits.h ), but this is intended for C and C++ programmers. A similar standard for scripting might be appropriate. The author of this document proposes restricting user-defined exit codes to the range 64 — 113 (in addition to 0 , for success), to conform with the C/C++ standard. This would allot 50 valid codes, and make troubleshooting scripts more straightforward.
All user-defined exit codes in the accompanying examples to this document now conform to this standard, except where overriding circumstances exist, as in Example 9-2 .
Issuing a $? from the command line after a shell script exits gives results consistent with the table above only from the Bash or sh prompt. Running the C-shell or tcsh may give different values in some cases.
Notes
Out of range exit values can result in unexpected exit codes. An exit value greater than 255 returns an exit code modulo 256 . For example, exit 3809 gives an exit code of 225 (3809 % 256 = 225).