Best practices when designating exit codes in command line programs

There is one thing which I frequently see in command line applications, especially those written in languages which have the concept of uncaught exceptions causing the program to terminate (although not exclusively) – people code in a way that makes their exit codes suck.

In Python, for example, the following sort of thing is a common sight in command line programs:

if args["--yes"] and args["--no"]:
    raise ArgumentError("Nonsense options: can't have both --yes and --no")

It doesn’t make sense to raise an exception in this manner. Exceptions are only really useful when you’re expecting that in, at least in some fringe situation, you’re planning on catching them. If you’re just planning to raise them to the user, use sys.exit() with a sensible exit code (and if it’s useful for them to have the traceback, give it to them separately).

This has a lot of benefits:

Many BSDs have attempted to actually standardise some set of exit failure conditions and their appropriate exit codes. To this effect, on many Unices sysexits.h actually defines some more “standard” exit codes, which you should probably prefer. In Python, you can access these via the os module:

$ python << 'EOF'
> import os
> import sys
>
> sys.exit(os.EX_CONFIG)
> EOF
$ echo "$?"
78

This method won’t work on systems that don’t support sysexits.h, though (like Windows, for example), and (as usual) not all Unices agree on what the standard should look like, so this can be tricky. My advice would be to look at the generally accepted constants (for example, what is suggested by FreeBSD), and then do something like the following when using them:

_EX_CONFIG = getattr(os, "EX_CONFIG", 78)

This can be a bit of a nightmare, though. The most important thing is that you make sure the exit codes your program returns are well documented, even if you don’t use the constants from sysexits.h.

In summary: