The exit status of a process is encoded as a value between 0 and 255, so that's all you can use as an exit code. If you pass a value outside that range, most shells use the remainder modulo 256. Some shells allow a wider range of integer values for functions.
The only rule for exit codes is that 0 means success and any other value means failure. This rule goes beyond unix: it's also a common convention on other operating systems (including DOS, Windows, and many embedded systems that have a notion of exit code, but VMS does things differently). In unix systems, it's baked into the shell's boolean constructs (
set -e, …), into
make, and followed by all the standard utilities. In POSIX C programs,
EXIT_SUCCESS is 0 and
EXIT_FAILURE is some non-zero value (usually 1).
There is no rule (de facto or de jure) regarding the choice of exit codes for failure. Only a few POSIX utilities mandate specific failure status codes:
! shell operator returns 1 if its operand returns 0. The
|| operator pass the status from the last command.
diff return 1 for different files and ≥2 for error conditions.
expr returns 1
if the expression evaluates to zero or null, 2 for an invalid expression, and ≥3 for other errors.
grep returns 1 for “not found” and ≥2 for error conditions. Many search commands follow this (but not
find, which returns 0 if no file matches).
mesg returns 0 for yes, 1 for no, and ≥2 for error.
patch returns 1 if a hunk was rejected and ≥2 for other errors.
sort -c returns 1 if the file data isn't sorted and ≥2 for errors.
localedef define some small values for specific errors.
There is a common, but not universal idea that larger values mean worse failures. For commands that test a boolean condition such as
grep (is this pattern present?) and
diff (are these files identical?), 1 means “no” and higher values indicate an error. In addition, values from 126 up are rarely used, as they are baked into the shell (and POSIX commands
- 126 and 127 indicate a failure to invoke an external command;
- values above 128 in
$? indicate a command that was terminated by a signal.
/usr/include/sysexits.h lists some values with their meanings, but it's from sendmail and I've never seen it outside programs that are unrelated to email delivery.
In summary, return 0 for success, and either 1 or 2 for failure. If you need to distinguish between failure cases, start at 1 and increase the value for worse failures.