This article is part number 8 of the CLI design series.

In the previous post, I provided common guidelines on how to implement a subcommand-based interface. It is now the time to look into the interface of those applications that implement a single command or function, which are actually quite abundant. Without going too far, you will encounter cp, ls and sudo, all of which are just a tiny sample of tools that fit this model.

General syntax

The general syntax of a single-command interface is simple:

tool [options] [arguments]

As you can notice from this definition, everything but the program name can be optional. If the tool requires no arguments, then it should expose a reasonable, non-erroneous behavior when executed by itself. On the other hand, if it requires arguments and none were provided, then obviously the tool should print an error.

Requesting help

Asking a program to show its builtin help, if any, is something that completely changes the behavior of what the program usually does. If that’s the case —and it is—, and if you have to adhere to the rule of only using flags to tune the normal behavior of the application: how can you expose a builtin help system in your interface?

Well… you cannot. You cannot offer a flag to show help because showing help is not a small tweak to the program behavior: it’s a switch to a totally different operational mode. You cannot dump help when the user makes a mistake because that is considered an unexpected and disturbing behavior. And you cannot add a special argument to request help because, then, you’d really be facing a subcommand-based interface.

So what can you do?

The first choice you have is to not provide a builtin help system at all. For simple tools —which is generally the case for single-command interfaces—, that may be the right thing to do. If you do not provide a builtin help system, then make the tool print a reference to the manual page when the user makes a mistake and ensure the manual page is kept up-to-date and accurate at all times.

For example, consider this sample invocation of a fictitious tool and the corresponding error message:

$ do_backups
do_backups: E: No backup type specified.
See do_backups(8) for more information.

The second choice is more pragmatic but you need to be open to foregoing some of the purity of the interface design in favor of slightly better usability. In this case, you can choose to abuse an option and expose the builtin help system via a --help flag. I personally don’t like this, but it is such a widespread alternative that your users will not be surprised about your choice and they will understand the interface pretty well. Mind you, the GNU Coding Standard recommends providing help by a --help flag so most of the GNU tools do this already.

Requesting the version number

We here face the same dilemma as with showing the builtin help: displaying the version number and exiting right away is a shift from the standard functionality of the tool and therefore you cannot offer this in a way that fits the simplified model of a single-command interface.

So, again, what are your options?

On the first hand, you can choose not to bundle the version information into the tool altogether. This actually is a very reasonable option for the majority of the cases: the version data can be obtained in many other ways such as by looking at the tool’s installed documentation or by querying the package manager for the details of the enclosing package.

Note: Be aware that bundling the version number—or, better, the build identifier—into the binary may prove to be of invaluable help if the binary is deployed to production systems: if the program crashes or exposes any bug, the operator will want an easy way to determine, univocally, the exact version of the binary that was running at the time of the problem. But don’t despair: under such circumstances, it is most useful to log the version number as part of normal operation instead of having to query it separately. (This is a topic that deserves much deeper explanations. Hint: stay tuned for the next series!)

On the other hand, you can choose to make an exception to the guidelines for using flags and introduce a --version option that displays your program’s version—just like the GNU Coding Standards also recommend.

With this post, you have now mastered the two major types of command-line interfaces: those that expose a variety of subcommands, described in the previous post; and those that expose a single command, described in this post. There really is no other style of interface for application invocation.

Comments from the original Blogger-hosted post: