I know $? from shell holds the last executed programs exit status.
For example, when I run below commands, I saw different status for different situation.
test$ hello
-bash: hello: command not found
test$ echo $?
127
test$ expr 1 / 0
expr: division by zero
test$ echo $?
2
I was wondering if there is any common exit status list in system or internet where i can get all the exit status with their descriptions. I found a list here, but some codes are missing, for example status code 127.
There can be no comprehensive list, because the meaning of command exit statuses is inherently command-specific. For a given command, you can usually get information about this on the respective command's manual page and Info documents.
In the case of
test$ hello
-bash: hello: command not found
test$ echo $?
127
the exit code 127 comes from bash, because the requested command itself couldn't be found.
In the case of
test$ expr 1 / 0
expr: division by zero
test$ echo $?
2
the exit code 2 comes from expr.
Some of these commands might be standardized or at least coordinated for several commands or a group of commands (e.g. "sh-compatible shells", I could imagine), but unless a command wants to conform to one of these conventions (and there are probably multiple conflicting conventions around), the command's authors are completely free to decide what they want their exit status codes to mean.
There's one important exception: All UNIX commands should adhere to this loose rule to be good citizens and provide meaningful composability (e.g. with pipes) on the command line:
0 means 'success' or "true"/"truthy"
non-0 means (in a very broad sense) 'failure' or 'non-success' or "false"/"falsy"
As you can see, this still leaves a lot of room for interpretation, which is perfectly intended, because these meanings must be specific to the context of the individual commands. (Consider e.g. the false command, that has the very purpose to "fail", thus always returns a non-0 exit code.)
The list you found describes return codes for system calls. System calls are when a program makes a request (in)to the kernel and are not the same as command invocation, thus these return codes are not (necessarily) the same as command exit codes.
The exit status is a numeric value that is returned by a program to the calling program or shell. In C programs, this is represented by the return value of the main() function or the value you give to exit(3). The only part of the number that matters are the least significant 8 bits, which means there are only values from 0 to 255.
Code Description
0 success
1-255 failure (in general)
126 the requested command (file) can't be executed (but was found)
127 command (file) not found
128 according to ABS it's used to report an invalid argument to the exit
builtin, but I wasn't able to verify that in the source code of Bash
(see code 255)
128 + N the shell was terminated by the signal N (also used like this by
various other programs)
255 wrong argument to the exit builtin (see code 128)
The lower codes 0 to 125 are not reserved and may be used for whatever the program likes to report. A value of 0 means successful termination, a value not 0 means unsuccessful termination. This behaviour (== 0, != 0) is also what Bash reacts on in some code flow control statements like if or while.
The above excerpt taken from Exit Status section from Bash Hackers Wiki.
The list you showed is really the closest possible thing to a "standardization", but frankly it looks more legit than it actually is. As far as I am aware of, almost no one pays much attention to these guys, but instead everyone names their own exit statuses:
Execute test1.sh
#!/bin/bash
a=10 ; [ "$a" -eq 9 ] && echo "Cool!" || exit 200
Output:
:~$ test1.sh
:~$ echo $?
200
Related
I am running the following Python code and checked the docs of subprocess but still din't find the code map:
import pkg_resources
from subprocess import call
packages = [dist.project_name for dist in pkg_resources.working_set]
call("sudo pip3 install --upgrade " + ' '.join(packages), shell=True)
Output: 2
What 2 means? Sometimes it's 0 or 1.
Every Linux command executed by the shell script or user, has an exit status.
The Linux man pages stats the exit statuses of each command.
0 exit status means the command was successful without any errors.
A non-zero (1-255 values) exit status means command was failure.
Typical signals are:
0 – Success.
1 – A built-in command failure.
2 – A syntax error has occurred.
3 – Signal received that is not trapped.
Certain exit status values have been reserved for special uses:
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.
A negative value -N indicates that the child was terminated by signal N (POSIX only).
The codes are platform specific, for example on Windows:
0 - Success
1 - Invalid function
2 - File not found
3 - Path not found
4 - Too many open files
etc...
From the python doc:
Some systems have a convention for assigning specific meanings to
specific exit codes, but these are generally underdeveloped; Unix
programs generally use 2 for command line syntax errors and 1 for all
other kind of errors.
"nice" command is returning value as 33. Please help me understand why nice is returning the value as 33. In some systems it works just fine giving return value as 0 but on a system it gives me return value as 33.
nice -n -10 <doing some process here>
returnValue=$?
Here returnValue gets a value as 33 in spite of 0.
It looks like nice(1) is returning the exit code of the niced command*. Try
nice bash -c 'exit 13' ; echo $?
and you get 13. So your niced command got an exit code of 33. We can't tell you how does that happen, because you don't tell what command are you nicing. BTW, some shells might have a nice builtin so you might use explicitly /usr/bin/nice instead of nice
Note *: even when nice fails (e.g. nice -n -12 id without being root), it says something to stderr but exit 0
I seem to have encountered a very, very strange inconsistency in the way both dash and bash check for error conditions with the errexit option.
Using both dash and bash without the set -e/set -o errexit option, the following program:
foo()
{
echo pre
bar=$(fail)
echo post
}
foo
will print the following (with slightly different error strings for dash):
pre
./foo.sh: line 4: fail: command not found
post
With the errexit option, it will print the following:
pre
./foo.sh: line 4: fail: command not found
Surprisingly, however, if bar is local, the program will always echo both pre and post. More specifically, using both dash and bash with our without the errexit option, the following program:
foo()
{
echo pre
local bar=$(fail)
echo post
}
foo
will print the following:
pre
./foo.sh: line 4: fail: command not found
post
In other words, it seems that the return value of a command substitution that is assigned to a local variable is not checked by errexit, but it is if the variable is global.
I would have been inclined to think this was simply a corner case bug, if it didn't happen with both shells. Since dash is specifically designed to be POSIX conformant I wonder if this behavior is actually specified by the POSIX standard, though I have a hard time imagining how that would make sense.
dash(1) has this to say about errexit:
If not interactive, exit immediately if any untested command fails. The exit status of a command is considered to be explicitly tested if the command is used to control an if, elif, while, or until; or if the command is the left hand operand of an “&&” or “||” operator.
bash(1) is somewhat more verbose, but I have a hard time making sense of it:
Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command (see SHELL GRAMMAR above), exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command's return value is being inverted with !. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR, if set, is executed before the shell exits. This option applies to the shell environment and each subshell environment separately (see COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.
If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.
TL;DR The exit status of local "hides" the exit status of any command substitutions appearing in one of its arguments.
The exit status of a variable assignment is poorly documented (or at least, I couldn't find any specifics in a quick skim of the various man pages and the POSIX spec). As far as I can tell, the exit status is taken as the exit status of the last command substitution that occurs in the value of the assignment, or 0 if there are no command substitutions. Non-final command substitutions appear to be included in the list of "tested" situations, as an assignment like
foo=$(false)$(true)
does not exit with errexit set.
local, however, is a command itself whose exit status is normally 0, independent of any command substitutions that occur in its arguments. That is, while
foo=$(false)
has an exit status of 1,
local foo=$(false)
will have an exit status of 0, with any command substitutions in an argument seemingly considered "tested" for the purposes of errexit.
Try this:
#!/bin/bash
set -e
foo()
{
echo pre
local bar
bar=$(fail)
echo post
}
foo
exit
!! OR !!
#!/bin/bash
foo()
{
set -e
echo pre
local bar
bar=$(fail)
echo post
}
foo
exit
OUTPUT:
$ ./errexit_function
pre
./errexit_function: line 8: fail: command not found
$ echo $?
127
As far as I can tell this is a work around for a bug in bash, but try this,
#!/bin/bash
set -e
foo()
{
echo true || return_value=$?
echo the command returned a value of ${return_value:-0}
$(fail) || return_value=$?
echo the command returned a value of ${return_value:-0}
echo post
}
foo
exit
I want to raise an error in a Bash script with message "Test cases Failed !!!". How to do this in Bash?
For example:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
This depends on where you want the error message be stored.
You can do the following:
echo "Error!" > logfile.log
exit 125
Or the following:
echo "Error!" 1>&2
exit 64
When you raise an exception you stop the program's execution.
You can also use something like exit xxx where xxx is the error code you may want to return to the operating system (from 0 to 255). Here 125 and 64 are just random codes you can exit with. When you need to indicate to the OS that the program stopped abnormally (eg. an error occurred), you need to pass a non-zero exit code to exit.
As #chepner pointed out, you can do exit 1, which will mean an unspecified error.
Basic error handling
If your test case runner returns a non-zero code for failed tests, you can simply write:
test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
printf '%s\n' "Test case x failed" >&2 # write error message to stderr
exit 1 # or exit $test_result
fi
Or even shorter:
if ! test_handler test_case_x; then
printf '%s\n' "Test case x failed" >&2
exit 1
fi
Or the shortest:
test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }
To exit with test_handler's exit code:
test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }
Advanced error handling
If you want to take a more comprehensive approach, you can have an error handler:
exit_if_error() {
local exit_code=$1
shift
[[ $exit_code ]] && # do nothing if no error code passed
((exit_code != 0)) && { # do nothing if error code is 0
printf 'ERROR: %s\n' "$#" >&2 # we can use better logging here
exit "$exit_code" # we could also check to make sure
# error code is numeric when passed
}
}
then invoke it after running your test case:
run_test_case test_case_x
exit_if_error $? "Test case x failed"
or
run_test_case test_case_x || exit_if_error $? "Test case x failed"
The advantages of having an error handler like exit_if_error are:
we can standardize all the error handling logic such as logging, printing a stack trace, notification, doing cleanup etc., in one place
by making the error handler get the error code as an argument, we can spare the caller from the clutter of if blocks that test exit codes for errors
if we have a signal handler (using trap), we can invoke the error handler from there
Error handling and logging library
Here is a complete implementation of error handling and logging:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Related posts
Error handling in Bash
The 'caller' builtin command on Bash Hackers Wiki
Are there any standard exit status codes in Linux?
BashFAQ/105 - Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?
Equivalent of __FILE__, __LINE__ in Bash
Is there a TRY CATCH command in Bash
To add a stack trace to the error handler, you may want to look at this post: Trace of executed programs called by a Bash script
Ignoring specific errors in a shell script
Catching error codes in a shell pipe
How do I manage log verbosity inside a shell script?
How to log function name and line number in Bash?
Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash?
There are a couple more ways with which you can approach this problem. Assuming one of your requirement is to run a shell script/function containing a few shell commands and check if the script ran successfully and throw errors in case of failures.
The shell commands in generally rely on exit-codes returned to let the shell know if it was successful or failed due to some unexpected events.
So what you want to do falls upon these two categories
exit on error
exit and clean-up on error
Depending on which one you want to do, there are shell options available to use. For the first case, the shell provides an option with set -e and for the second you could do a trap on EXIT
Should I use exit in my script/function?
Using exit generally enhances readability In certain routines, once you know the answer, you want to exit to the calling routine immediately. If the routine is defined in such a way that it doesn’t require any further cleanup once it detects an error, not exiting immediately means that you have to write more code.
So in cases if you need to do clean-up actions on script to make the termination of the script clean, it is preferred to not to use exit.
Should I use set -e for error on exit?
No!
set -e was an attempt to add "automatic error detection" to the shell. Its goal was to cause the shell to abort any time an error occurred, but it comes with a lot of potential pitfalls for example,
The commands that are part of an if test are immune. In the example, if you expect it to break on the test check on the non-existing directory, it wouldn't, it goes through to the else condition
set -e
f() { test -d nosuchdir && echo no dir; }
f
echo survived
Commands in a pipeline other than the last one, are immune. In the example below, because the most recently executed (rightmost) command's exit code is considered ( cat) and it was successful. This could be avoided by setting by the set -o pipefail option but its still a caveat.
set -e
somecommand that fails | cat -
echo survived
Recommended for use - trap on exit
The verdict is if you want to be able to handle an error instead of blindly exiting, instead of using set -e, use a trap on the ERR pseudo signal.
The ERR trap is not to run code when the shell itself exits with a non-zero error code, but when any command run by that shell that is not part of a condition (like in if cmd, or cmd ||) exits with a non-zero exit status.
The general practice is we define an trap handler to provide additional debug information on which line and what cause the exit. Remember the exit code of the last command that caused the ERR signal would still be available at this point.
cleanup() {
exitcode=$?
printf 'error condition hit\n' 1>&2
printf 'exit code returned: %s\n' "$exitcode"
printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
printf 'command present on line: %d' "${BASH_LINENO[0]}"
# Some more clean up code can be added here before exiting
exit $exitcode
}
and we just use this handler as below on top of the script that is failing
trap cleanup ERR
Putting this together on a simple script that contained false on line 15, the information you would be getting as
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
The trap also provides options irrespective of the error to just run the cleanup on shell completion (e.g. your shell script exits), on signal EXIT. You could also trap on multiple signals at the same time. The list of supported signals to trap on can be found on the trap.1p - Linux manual page
Another thing to notice would be to understand that none of the provided methods work if you are dealing with sub-shells are involved in which case, you might need to add your own error handling.
On a sub-shell with set -e wouldn't work. The false is restricted to the sub-shell and never gets propagated to the parent shell. To do the error handling here, add your own logic to do (false) || false
set -e
(false)
echo survived
The same happens with trap also. The logic below wouldn't work for the reasons mentioned above.
trap 'echo error' ERR
(false)
Here's a simple trap that prints the last argument of whatever failed to STDERR, reports the line it failed on, and exits the script with the line number as the exit code. Note these are not always great ideas, but this demonstrates some creative application you could build on.
trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
I put that in a script with a loop to test it. I just check for a hit on some random numbers; you might use actual tests. If I need to bail, I call false (which triggers the trap) with the message I want to throw.
For elaborated functionality, have the trap call a processing function. You can always use a case statement on your arg ($_) if you need to do more cleanup, etc. Assign to a var for a little syntactic sugar -
trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
throw=false
raise=false
while :
do x=$(( $RANDOM % 10 ))
case "$x" in
0) $throw "DIVISION BY ZERO" ;;
3) $raise "MAGIC NUMBER" ;;
*) echo got $x ;;
esac
done
Sample output:
# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6
Obviously, you could
runTest1 "Test1 fails" # message not used if it succeeds
Lots of room for design improvement.
The draw backs include the fact that false isn't pretty (thus the sugar), and other things tripping the trap might look a little stupid. Still, I like this method.
You have 2 options: Redirect the output of the script to a file, Introduce a log file in the script and
Redirecting output to a file:
Here you assume that the script outputs all necessary info, including warning and error messages. You can then redirect the output to a file of your choice.
./runTests &> output.log
The above command redirects both the standard output and the error output to your log file.
Using this approach you don't have to introduce a log file in the script, and so the logic is a tiny bit easier.
Introduce a log file to the script:
In your script add a log file either by hard coding it:
logFile='./path/to/log/file.log'
or passing it by a parameter:
logFile="${1}" # This assumes the first parameter to the script is the log file
It's a good idea to add the timestamp at the time of execution to the log file at the top of the script:
date '+%Y%-m%d-%H%M%S' >> "${logFile}"
You can then redirect your error messages to the log file
if [ condition ]; then
echo "Test cases failed!!" >> "${logFile}";
fi
This will append the error to the log file and continue execution. If you want to stop execution when critical errors occur, you can exit the script:
if [ condition ]; then
echo "Test cases failed!!" >> "${logFile}";
# Clean up if needed
exit 1;
fi
Note that exit 1 indicates that the program stop execution due to an unspecified error. You can customize this if you like.
Using this approach you can customize your logs and have a different log file for each component of your script.
If you have a relatively small script or want to execute somebody else's script without modifying it to the first approach is more suitable.
If you always want the log file to be at the same location, this is the better option of the 2. Also if you have created a big script with multiple components then you may want to log each part differently and the second approach is your only option.
I often find it useful to write a function to handle error messages so the code is cleaner overall.
# Usage: die [exit_code] [error message]
die() {
local code=$? now=$(date +%T.%N)
if [ "$1" -ge 0 ] 2>/dev/null; then # assume $1 is an error code if numeric
code="$1"
shift
fi
echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
exit $code
}
This takes the error code from the previous command and uses it as the default error code when exiting the whole script. It also notes the time, with microseconds where supported (GNU date's %N is nanoseconds, which we truncate to microseconds later).
If the first option is zero or a positive integer, it becomes the exit code and we remove it from the list of options. We then report the message to standard error, with the name of the script, the word "ERROR", and the time (we use parameter expansion to truncate nanoseconds to microseconds, or for non-GNU times, to truncate e.g. 12:34:56.%N to 12:34:56). A colon and space are added after the word ERROR, but only when there is a provided error message. Finally, we exit the script using the previously determined exit code, triggering any traps as normal.
Some examples (assume the code lives in script.sh):
if [ condition ]; then die 123 "condition not met"; fi
# exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"
$command |grep -q condition || die 1 "'$command' lacked 'condition'"
# exit code 1, "script.sh: ERROR at 14:58:55.825626: 'foo' lacked 'condition'"
$command || die
# exit code comes from command's, message "script.sh: ERROR at 14:59:15.575089"
A process is considered to have completed correctly in Linux if its exit status was 0.
I've seen that segmentation faults often result in an exit status of 11, though I don't know if this is simply the convention where I work (the applications that failed like that have all been internal) or a standard.
Are there standard exit codes for processes in Linux?
Part 1: Advanced Bash Scripting Guide
As always, the Advanced Bash Scripting Guide has great information:
(This was linked in another answer, but to a non-canonical URL.)
1: Catchall for general errors
2: Misuse of shell builtins (according to Bash documentation)
126: Command invoked cannot execute
127: "command not found"
128: Invalid argument to exit
128+n: Fatal error signal "n"
255: Exit status out of range (exit takes only integer args in the range 0 - 255)
Part 2: sysexits.h
The ABSG references sysexits.h.
On Linux:
$ find /usr -name sysexits.h
/usr/include/sysexits.h
$ cat /usr/include/sysexits.h
/*
* Copyright (c) 1987, 1993
* The Regents of the University of California. All rights reserved.
(A whole bunch of text left out.)
#define EX_OK 0 /* successful termination */
#define EX__BASE 64 /* base value for error messages */
#define EX_USAGE 64 /* command line usage error */
#define EX_DATAERR 65 /* data format error */
#define EX_NOINPUT 66 /* cannot open input */
#define EX_NOUSER 67 /* addressee unknown */
#define EX_NOHOST 68 /* host name unknown */
#define EX_UNAVAILABLE 69 /* service unavailable */
#define EX_SOFTWARE 70 /* internal software error */
#define EX_OSERR 71 /* system error (e.g., can't fork) */
#define EX_OSFILE 72 /* critical OS file missing */
#define EX_CANTCREAT 73 /* can't create (user) output file */
#define EX_IOERR 74 /* input/output error */
#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
#define EX_PROTOCOL 76 /* remote error in protocol */
#define EX_NOPERM 77 /* permission denied */
#define EX_CONFIG 78 /* configuration error */
#define EX__MAX 78 /* maximum listed value */
8 bits of the return code and 8 bits of the number of the killing signal are mixed into a single value on the return from wait(2) & co..
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
int main() {
int status;
pid_t child = fork();
if (child <= 0)
exit(42);
waitpid(child, &status, 0);
if (WIFEXITED(status))
printf("first child exited with %u\n", WEXITSTATUS(status));
/* prints: "first child exited with 42" */
child = fork();
if (child <= 0)
kill(getpid(), SIGSEGV);
waitpid(child, &status, 0);
if (WIFSIGNALED(status))
printf("second child died with %u\n", WTERMSIG(status));
/* prints: "second child died with 11" */
}
How are you determining the exit status? Traditionally, the shell only stores an 8-bit return code, but sets the high bit if the process was abnormally terminated.
$ sh -c 'exit 42'; echo $?
42
$ sh -c 'kill -SEGV $$'; echo $?
Segmentation fault
139
$ expr 139 - 128
11
If you're seeing anything other than this, then the program probably has a SIGSEGV signal handler which then calls exit normally, so it isn't actually getting killed by the signal. (Programs can chose to handle any signals aside from SIGKILL and SIGSTOP.)
None of the older answers describe exit status 2 correctly. Contrary to what they claim, status 2 is what your command line utilities actually return when called improperly. (Yes, an answer can be nine years old, have hundreds of upvotes, and still be wrong.)
Here is the real, long-standing exit status convention for normal termination, i.e. not by signal:
Exit status 0: success
Exit status 1: "failure", as defined by the program
Exit status 2: command line usage error
For example, diff returns 0 if the files it compares are identical, and 1 if they differ. By long-standing convention, unix programs return exit status 2 when called incorrectly (unknown options, wrong number of arguments, etc.) For example, diff -N, grep -Y or diff a b c will all result in $? being set to 2. This is and has been the practice since the early days of Unix in the 1970s.
The accepted answer explains what happens when a command is terminated by a signal. In brief, termination due to an uncaught signal results in exit status 128+[<signal number>. E.g., termination by SIGINT (signal 2) results in exit status 130.
Notes
Several answers define exit status 2 as "Misuse of bash builtins". This applies only when bash (or a bash script) exits with status 2. Consider it a special case of incorrect usage error.
In sysexits.h, mentioned in the most popular answer, exit status EX_USAGE ("command line usage error") is defined to be 64. But this does not reflect reality: I am not aware of any common Unix utility that returns 64 on incorrect invocation (examples welcome). Careful reading of the source code reveals that sysexits.h is aspirational, rather than a reflection of true usage:
* This include file attempts to categorize possible error
* exit statuses for system programs, notably delivermail
* and the Berkeley network.
* Error numbers begin at EX__BASE [64] to reduce the possibility of
* clashing with other exit statuses that random programs may
* already return.
In other words, these definitions do not reflect the common practice at the time (1993) but were intentionally incompatible with it. More's the pity.
'1': Catch-all for general errors
'2': Misuse of shell builtins (according to Bash documentation)
'126': Command invoked cannot execute
'127': "command not found"
'128': Invalid argument to exit
'128+n': Fatal error signal "n"
'130': Script terminated by Ctrl + C
'255': Exit status out of range
This is for Bash. However, for other applications, there are different exit codes.
There are no standard exit codes, aside from 0 meaning success. Non-zero doesn't necessarily mean failure either.
Header file stdlib.h does define EXIT_FAILURE as 1 and EXIT_SUCCESS as 0, but that's about it.
The 11 on segmentation fault is interesting, as 11 is the signal number that the kernel uses to kill the process in the event of a segmentation fault. There is likely some mechanism, either in the kernel or in the shell, that translates that into the exit code.
Header file sysexits.h has a list of standard exit codes. It seems to date back to at least 1993 and some big projects like Postfix use it, so I imagine it's the way to go.
From the OpenBSD man page:
According to style(9), it is not good practice to call exit(3) with arbitrary values to indicate a failure condition when ending a program. Instead, the predefined exit codes from sysexits should be used, so the caller of the process can get a rough estimation about the failure class without looking up the source code.
To a first approximation, 0 is success, non-zero is failure, with 1 being general failure, and anything larger than one being a specific failure. Aside from the trivial exceptions of false and test, which are both designed to give 1 for success, there's a few other exceptions I found.
More realistically, 0 means success or maybe failure, 1 means general failure or maybe success, 2 means general failure if 1 and 0 are both used for success, but maybe success as well.
The diff command gives 0 if files compared are identical, 1 if they differ, and 2 if binaries are different. 2 also means failure. The less command gives 1 for failure unless you fail to supply an argument, in which case, it exits 0 despite failing.
The more command and the spell command give 1 for failure, unless the failure is a result of permission denied, nonexistent file, or attempt to read a directory. In any of these cases, they exit 0 despite failing.
Then the expr command gives 1 for success unless the output is the empty string or zero, in which case, 0 is success. 2 and 3 are failure.
Then there's cases where success or failure is ambiguous. When grep fails to find a pattern, it exits 1, but it exits 2 for a genuine failure (like permission denied). klist also exits 1 when it fails to find a ticket, although this isn't really any more of a failure than when grep doesn't find a pattern, or when you ls an empty directory.
So, unfortunately, the Unix powers that be don't seem to enforce any logical set of rules, even on very commonly used executables.
Programs return a 16 bit exit code. If the program was killed with a signal then the high order byte contains the signal used, otherwise the low order byte is the exit status returned by the programmer.
How that exit code is assigned to the status variable $? is then up to the shell. Bash keeps the lower 7 bits of the status and then uses 128 + (signal nr) for indicating a signal.
The only "standard" convention for programs is 0 for success, non-zero for error. Another convention used is to return errno on error.
Standard Unix exit codes are defined by sysexits.h, as David mentioned.
The same exit codes are used by portable libraries such as Poco - here is a list of them:
Class Poco::Util::Application, ExitCode
A signal 11 is a SIGSEGV (segment violation) signal, which is different from a return code. This signal is generated by the kernel in response to a bad page access, which causes the program to terminate. A list of signals can be found in the signal man page (run "man signal").
When Linux returns 0, it means success. Anything else means failure. Each program has its own exit codes, so it would been quite long to list them all...!
About the 11 error code, it's indeed the segmentation fault number, mostly meaning that the program accessed a memory location that was not assigned.
Some are convention, but some other reserved ones are part of POSIX standard.
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.
See the section RATIONALE of man 1p exit.