NOTE: If you aren’t interested in the commentary and just want to learn about the correct syntax for usage strings, you can skip directly to ‘Usage Syntax’.
To anyone who uses the command line on a UNIX-like system, usage strings are an absolutely essential feature to any tool you use that you probably never think too much about. In a simple and concise syntax they’re able to tell the user what options a tool accepts, what combinations of those options are legal, what arguments are optional, and so on.
Despite how widespread they are and how useful they are, a huge amount of developers still do not know how to write proper usage strings, even in manual pages, where they are most important. It is for this reason that I am writing this post, to hopefully teach more people how to write these strings properly.
On top of that, those aforementioned developers often don’t understand the actual purpose of the usage string. The purpose of the usage string is to remind the user of the software, what the option they mistyped was supposed to be, or if it takes an argument they forgot to pass. The usage string is not the comprehensive documentation of the interface of your program – that is the job of the manual page. Documentation goes in manual pages, something that an increasing number of projects are forgetting.
What You Shouldn’t Do
First, let’s take a look at an example of a bad usage string, and explore why it’s bad:
$ average-gnu-command
USAGE: average-gnu-command <OPTIONS> [FILES...]
OPTIONS:
--help Print help.
--shuffle=[={SEED|random|reverse|none}]
Perform shuffle of prerequisites and goals.
-1 STRING, --my-fake-flag-1=STRING
Another super long description.
# 50 other options omitted...So the first but also the least important problem here is the capitalization. This obviously does not affect the usability at all so it’s mostly a non-issue, but SCREAMING AT THE USER LIKE THIS is going to look sloppy to a number of users. Almost all vendors of system utilities (the BSDs, MacOS, Busybox, Util-Linux, etc.) will write their usage strings in lowercase. The main exception here seems to be the GNU coreutils. Again though, this doesn’t matter too much and is mostly a matter of style.
Now let’s get into some proper usability issues!
Too Much Output
In the example above, average-gnu-command decided it
was going to spit out documentation for over 50 different
options to us out on to the terminal. This might sound
awesome, but they just gave us almost an entire manual pages
worth of content in a terminal that might be running in a
split window on a laptop. This is not helpful,
because if the option I wanted was actually there, there is
a pretty good chance it did not even fit on my screen, and I
now need to scroll my terminal trying to find what I want.
So now either I need to scroll, or I can re-run the command with the output piped through a pager1:
$ average-gnu-command 2>&1 | lessThis still sucks; scrolling through a bunch of monospace,
grey text is not very easy for human eyes to parse.
Especially if you already suffer from some form of visual
impairment. You know what does have syntax
highlighting, consistent easy-to-read formatting, and paging
abilities? The man(1) command2 –
please just write actual descriptive manual pages for your
tools.
Let me repeat that one more time.
WRITE ACTUAL MANUAL PAGES FOR YOUR TOOLS!
Preferably in mdoc(7), but
man(7) or even scdoc(5) is
better than no manual.
Like seriously, what the fuck is this? This is the output
of ls --help on my desktop monitor that is
vertically maximized – not even a laptop! More than half of
the options are not even visible.

Mutual Exclusion
Let me ask you a question: of all those flags we saw above,
which ones can I use together? Which ones can I not
use together? Don’t know? Yeah, me neither. The reason we
don’t know is because the usage string thought that all we
needed to know was that average-gnu-command accepted
<OPTIONS>… thanks Captain Obvious!
Required Options
Now let me ask another question: which flags are required? Once again, we don’t know, because the author of the software we want to use didn’t bother to tell us.
What You Should Do
Now that we know about some of the usability problems associated with the usage strings of many command-line tools that exist today, let’s look at two good examples of high-quality usage strings, and how they concisely communicate important information to us. I came across both of these examples while working on my new OpenBSD server.
$ wc -x
wc: unknown option -- x
usage: wc [-c | -m] [-hlw] [file ...]
$ sed -x
sed: unknown option -- x
usage: sed [-aEnru] [-i[extension]] command [file ...]
sed [-aEnru] [-e command] [-f command_file] [-i[extension]] [file ...]Let’s focus on the wc(1) command first.
Based on usage string that is only one line long, we
can determine the follow facts about the usage of the
program:
- It accepts 5 different flags.
- The
-cand-mflags are mutually exclusive. - It accepts 0 or more file arguments after any given flags.
Notice how there is no noisy help documentation printed
which describes the behaviour of each option flag. This is
redundant and unnecessary, because these flags are all
already documented in the extensive and high-quality
wc(1) manual.
The usage string for sed(1) is also helpful.
By just looking at it for less than a second we can
determine facts such as:
- The argument to
-iis optional. - The positional
commandargument is mutually exclusive with the-eand-fflags.
Another thing to pay attention to is how in the sed(1) example, we actually see two usage
lines. When using vertical bars to represent
mutually-exclusive alternatives is too noisy or not
practical we can always just use extra usage lines.
Now by this point the average Hacker News reader will
exclaim something along the lines of ‘This is terrible! How
can I know what -E even does?’. Well dear reader,
you know by reading the manual page. Again…
usage strings are not a substitute for actual
documentation, they are nothing more than a quick reminder
to someone who already knows how to use the software, on why
what they tried to do was wrong.
So now that we’ve seen an example of good usage strings, let’s go more in-depth on the specific syntax.
Usage Syntax
NOTE: There is no stict standard specifying a specific syntax. The rules documented below are the general consensus amongst the OS utility vendors of various UNIX-like systems, and POSIX. When there is disagreement on a syntactical detail, the alternatives will be listed.
The first line of a usage string begins with the string ‘usage:’ followed by a space. Subsequent usage strings are aligned using spaces. When the usage line gets too long, continue on the next line space aligned to the options from the previous line.
NOTE: On the various BSDs and MacOS, ‘usage’ is typically written in all lowercase while on Linux ‘Usage’ with a capital ‘U’ is preferred by GNU, Util-Linux, and Busybox. Both variants are used by POSIX in their manual pages.
usage: foo [-abc] [--a-long-option] [--another-long-option]
[--final-option] directory file
foo [-de]Anything optional should be wrapped in brackets. This includes options, arguments, arguments to options, or even parts of arguments.
usage: foo [-abc] [-i[optional]] [--long-option[=optional]]
ftp://[user:password@]host[:port]/file[/] ...Short options are grouped together and prefixed with a single ‘-’. Long options, and short options that accept arguments are listed individually. The following sorting rules apply:
- Short options that don’t accept arguments come first.
- Short options that accept arguments come second.
- Long options come third.
- Required options come fourth.
- Positional arguments come last.
- Options are sorted with numbers coming first, and capital letters coming before their lowercase variant.
NOTE: As an exception to rule 4, when the required
option denotes the primary mode of the program, or
significantly alters the program’s behaviour, it is
generally written at the beginning such as with
ssh-keygen(1).
usage: foo [-0abcDdz] [-e foo] [-f bar] [--bar] -x foo barRequired short option arguments are separated by a space, optional short option arguments are not separated by a space, and long option arguments are separated by an equals sign.
usage: foo [-e foo] [-i[bar]] [--long-1=foo] [--long-2[=bar]]Mutually exclusive options are grouped together, and separated by a space-padded bar. When an option’s argument must be one of a series of options, the different alternatives are separated by a bar and no spaces.
NOTE: To avoid an overly complex usage string, mutually exclusive options are usually denoted by writing multiple usage strings.
NOTE: Sometimes to avoid repetition or with more
complicated alternatives, braces are used, such as in
FreeBSD’s crontab(1). This is however,
quite rare.
usage: foo [-a | -b] [-T ascii|html|pdf] [--color=always|auto|never]If an argument can be passed one or more times it should be suffixed with a space and an ellipsis. If the argument is accepted zero or more times, wrap it in brackets.
usage: foo [-ab] [file ...]
foo -c file ...