I recently came across this list of Exit Codes With Special Meanings from the Advanced Bash-Scripting Guide. They refer to these codes as being reserved and recommend that:
According to the above table, exit codes 1-2, 126-165, and 255 have special
meanings, and should therefore be avoided for user-specified exit
parameters.
A while ago, I wrote a script which used the following exit status codes:
- 0 – success
- 1 – incorrect hostname
- 2 – invalid arguments specified
- 3 – insufficient user privileges
When I wrote the script I wasn’t aware of any special exit codes so I simply started at 1 for the first error condition, and incremented the exit status for each successive error type.
I wrote the script with the intention that at a later stage it could be called by other scripts (which could check for the non-zero exit codes). I haven’t actually done that yet; so far I’ve only run the script from my interactive shell (Bash) and I was wondering what / if any problems could be caused by using my custom exit codes. How relevant/important is the recommendation from the Advanced Bash-Scripting Guide?
I couldn’t find any corroborating advice in the Bash documentation; its section on Exit Status simply lists the exit codes used by Bash but doesn’t state that any of these are reserved or warn against using them for your own scripts/programs.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
No exit code has a special meaning, but the value in $? may have a special meaning.
The way Bourne Shell and ksh93 handled and forwarded exit codes and error situations to the shell variable $? is the problem. In contrary to what you list, only the following values for $? have a special meaning:
- 126 Could not execute the binary even though it exists
- 127 The specified binary does not exist
- 128 exit status was == 0 but some unspecified problem exists
In addition, there is an unspecified shell and platform-specific range of $? codes > 128 that is reserved for a program that was interrupted by a signal:
- Bourne Shell bash and ksh88 use 128 + signal number
- ksh93 uses 256 + signal number.
Other values do not give problems as they may be distinguished from the shell-special $? values.
In particular, the values 1 and 2 are not used for special conditions but are just exit codes used by builtin commands that could act the same when they are no builtins. So is seems that the pointer to the bash scripting guide you provided is not a good manual as it just lists codes used by bash without commenting whether a specific code is a special value that should be avoided for own scripts.
Newer versions of the Bourne Shell use waitid() instead of waitpid() to wait for the program to exit and waitid() (introduced 1989 for SVr4) uses a better syscall interface (similar to what UNOS used in 1980 already).
As newer Bourne Shell versions encode the exit reason in a separate variable ${.sh.code} / ${.sh.codename} than the exit code that is in ${.sh.status}/ ${.sh.termsig}, see http://schillix.sourceforge.net/man/man1/bosh.1.html, the exit code is not overloaded with special states, and, as a result from using `waitid(), the Bourne Shell now supports returning all 32 bits of the exit code – not just the low 8 bits.
BTW: be careful not to exit(256) or similar from a C-program or shell script, as this results in $? being interpreted as 0 in a classic shell.
Method 2
There have been several attempts to standardize the meanings of process exit codes. In addition to the one you mention, I know of:
-
the BSDs have
sysexits.hwhich defines meanings for values from 64 on up. -
GNU
grepdocuments that exit code 0 means at least one match was found, 1 means no matches were found, and 2 means an I/O error occurred; this convention is obviously also useful for other programs for which the distinction between “nothing went wrong but I didn’t find anything” and “an I/O error occurred” is meaningful. -
Many implementations of the C library function
systemuse exit code 127 to indicate the program doesn’t exist or failed to start. -
On Windows,
NTSTATUScodes (which are inconveniently scattered all over the 32-bit number space) may be used as exit codes, particularly the ones that indicate a process was terminated due to catastrophic misbehavior (e.g.STATUS_STACK_OVERFLOW).
You can’t count on any given program obeying any particular one of these conventions. The only reliable rule is that exit code 0 is success and anything else is some sort of failure. (Note that C89’s EXIT_SUCCESS is not guaranteed to have the value zero; however, exit(0) is required to behave identically to exit(EXIT_SUCCESS) even if the values are not the same.)
Method 3
For shell scripting, I sometimes in-source the shell equivalent of sysexist.h with shell-reserved exit codes (prefixed with S_EX_), which I’ve named exit.sh
It’s basically:
EX_OK=0 # successful termination EX__BASE=64 # base value for error messages EX_USAGE=64 # command line usage error EX_DATAERR=65 # data format error EX_NOINPUT=66 # cannot open input EX_NOUSER=67 # addressee unknown EX_NOHOST=68 # host name unknown EX_UNAVAILABLE=69 # service unavailable EX_SOFTWARE=70 # internal software error EX_OSERR=71 # system error (e.g., can't fork) EX_OSFILE=72 # critical OS file missing EX_CANTCREAT=73 # can't create (user) output file EX_IOERR=74 # input/output error EX_TEMPFAIL=75 # temp failure; user is invited to retry EX_PROTOCOL=76 # remote error in protocol EX_NOPERM=77 # permission denied EX_CONFIG=78 # configuration error EX__MAX=78 # maximum listed value #System errors S_EX_ANY=1 #Catchall for general errors S_EX_SH=2 #Misuse of shell builtins (according to Bash documentation); seldom seen S_EX_EXEC=126 #Command invoked cannot execute Permission problem or command is not an executable S_EX_NOENT=127 #"command not found" illegal_command Possible problem with $PATH or a typo S_EX_INVAL=128 #Invalid argument to exit exit 3.14159 exit takes only integer args in the range 0 - 255 (see first footnote) #128+n Fatal error signal "n" kill -9 $PPID of script $? returns 137 (128 + 9) #255* Exit status out of range exit -1 exit takes only integer args in the range 0 - 255 S_EX_HUP=129 S_EX_INT=130 #...
And can be generated with:
#!/bin/sh
src=/usr/include/sysexits.h
echo "# Generated from "$src""
echo "# Please inspect the source file for more detailed descriptions"
echo
< "$src" sed -rn 's/^#define *(w+)s*(d*)/1=2/p'| sed 's:/*:#:; s:*/::'
cat<<'EOF'
#System errors
S_EX_ANY=1 #Catchall for general errors
S_EX_SH=2 #Misuse of shell builtins (according to Bash documentation); seldom seen
S_EX_EXEC=126 #Command invoked cannot execute Permission problem or command is not an executable
S_EX_NOENT=127 #"command not found" illegal_command Possible problem with $PATH or a typo
S_EX_INVAL=128 #Invalid argument to exit exit 3.14159 exit takes only integer args in the range 0 - 255 (see first footnote)
#128+n Fatal error signal "n" kill -9 $PPID of script $? returns 137 (128 + 9)
#255* Exit status out of range exit -1 exit takes only integer args in the range 0 - 255
EOF
$(which kill) -l |tr ' ' 'n'| awk '{ printf "S_EX_%s=%sn", $0, 128+NR; }'
I don’t use it much, though, but what I do use is a shell function that inverses error codes to their string formats. I’ve named it exit2str. Assuming you’ve named the above exit.sh generator exit.sh.sh, the code for exit2str can be generated with (exit2str.sh.sh) :
#!/bin/sh
echo '
exit2str(){
case "$1" in'
./exit.sh.sh | sed -nEe's|^(S_)?EX_(([^_=]+_?)+)=([0-9]+).*|4) echo "12";;|p'
echo "
esac
}"
I use this in the PS1 of my interactive shell so that after each command I run, I can see its exit status and its string form (if it does have a known string form):
[15:58] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4939233c2439092528393d2639">[email protected]</a>:~ (0=OK)$ [15:59] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="cdbda7b8a0bd8da1acbdb9a2bd">[email protected]</a>:~ (0=OK)$ fdsaf fdsaf: command not found [15:59] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1868726d7568587479686c7768">[email protected]</a>:~ (127=S_NOENT)$ sleep sleep: missing operand Try 'sleep --help' for more information. [15:59] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="433329362e33032f2233372c33">[email protected]</a>:~ (1=S_ANY)$ sleep 100 ^C [15:59] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c6b6acb3abb686aaa7b6b2a9b6">[email protected]</a>:~ (130=S_INT)$ sleep 100 ^Z [1]+ Stopped sleep 100 [15:59] <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="eb9b819e869bab878a9b9f849b">[email protected]</a>:~ (148=S_TSTP)$
To get these, you need an insourcable for the exit2str function:
$ ./exit2str.sh.sh > exit2str.sh #Place this somewhere in your PATH
and then use it in your ~/.bashrc to save and translate the exit code on each command prompt and display it your prompt (PS1):
# ...
. exit2str.sh
PROMPT_COMMAND='lastStatus=$(st="$?"; echo -n "$st"; str=$(exit2str "$st") && echo "=$str"); # ...'
PS1="$PS1"'n($lastStatus)$'
# ...
It’s quite handy for observing how some programs follow the exit code conventions and some don’t, for learning about exit code conventions, or just for being able to see what’s going on more readily.
Having been using it for some time, I can say that many system-oriented shell scripts do follow the conventions. EX_USAGE is particularly quite common, although other codes, not much. I try to follow the conventions from time to time, although there’s always $S_EX_ANY (1) for lazy people (I am one).
Method 4
The best reference I could find was this: http://tldp.org/LDP/abs/html/exitcodes.html
According to this:
1 is a general catchall for errors, and I’ve always seen it used for user defined errors.
2 is for misuse of shell built ins, such as a syntax error
To answer your question directly your script will be fine using the reserved error codes, it will function as expected assuming you handle the error based on the error code = 1/2/3.
However, it would possibly be confusing if you encounter anyone who knows and uses the reserved error codes, which seems quite rare.
Another option available to you is to echo the error if there is one and then exit, assuming your script follows the Linux convention of “no news is good news” and echo’s nothing on success.
if [ $? -ne 0 ];then
echo "Error type"
exit 1
fi
Method 5
As long as you document your exit codes so that you remember them a year from now when you have to come back and tweak the script you’ll be fine. The idea of “reserved exit codes” doesn’t really apply anymore other than to say it’s customary to use 0 as a success code and anything else as a failure code.
Method 6
Based on the answers I’ve received (it was difficult to pick one over the others), it isn’t harmful to indicate certain types of errors by using an exit code that Bash also uses. Bash (or any other Unix shell) won’t do anything special (such as running exception handlers) if a user script exits with one of these error codes.
It seems that the author of the Advanced Bash-Scripting Guide agrees with the BSD attempts to standardise exit codes (sysexits.h) and is simply recommending that when users write shell scripts, they don’t specify exit codes that conflict with pre-defined exit codes already in use, i.e., they restrict their custom exit codes to the 50 available status codes in the range 64-113.
I appreciate the idea (and the rationale) but I’d have preferred if the author was more explicit that it’s not harmful to ignore the advice – aside from cases where the consumer of a script is checking for errors such as the cited example of 127 (command not found).
Relevant POSIX specifications
I researched what POSIX has to say about exit codes and the POSIX specification seems to concur with the author of the Advanced Bash-Scripting Guide. I’ve quoted the relevant POSIX specifications (emphasis mine):
Each command has an exit status that can influence the behavior of other shell commands. The exit status of commands that are not utilities is documented in this section. The exit status of the standard utilities is documented in their respective sections.
If a command is not found, the exit status shall be 127. If the command name is found, but it is not an executable utility, the exit status shall be 126. Applications that invoke utilities without using the shell should use these exit status values to report similar errors.
If a command fails during word expansion or redirection, its exit status shall be greater than zero.
Internally, for purposes of deciding whether a command exits with a non-zero exit status, the shell shall recognize the entire status value retrieved for the command by the equivalent of the wait() function WEXITSTATUS macro (as defined in the System Interfaces volume of POSIX.1-2008). When reporting the exit status with the special parameter ‘?’, the shell shall report the full eight bits of exit status available. The exit status of a command that terminated because it received a signal shall be reported as greater than 128.
As explained in other sections, certain exit status values have been reserved for special uses and should be used by applications only for those purposes:
126– A file to be executed was found, but it was not an executable utility.127– A utility to be executed was not found.>128– A command was interrupted by a signal.
Further information
For what it’s worth, I was able to verify all but one of the list of Exit Codes With Special Meanings. This table of exit codes is useful as it provides more details – and examples of how to generate the error codes documented in the Bash reference.
Attempt to generate exit status of 128
Using Bash versions 3.2.25 and 4.2.46, I tried to throw a 128 Invalid argument to exit error but each time I received a 255 (Exit status out of range). E.g., if exit 3.14159 is executed as part of a shell script or in an interactive child shell, the shell exits with a code of 255:
$ exit 3.14159 exit bash: exit: 3.14159: numeric argument required
For even more fun, I also tried running a simple C program but in this case, it seems that the exit(3) function simply converted the float to an int (3 in this case) before exiting:
#include <stdlib.h>
main()
{
exit(3.14159);
}
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0