There are few articles on the design of command-line interfaces (CLIs), even if plenty of articles on designing graphical user interfaces (GUIs) exist. This article is an attempt at presenting some of the most important guidelines for CLI design.
The article assumes the command-line utilities are to be used on a *nix system (e.g. GNU/Linux, BSD, Mac OS X, Unix), and it will frequently reference to common tools on such systems.
Types of command-line interfaces
There are three major sorts of command-line interfaces:
- Non-interactive
- Interactive, line-based
- Text-based user interface
Non-interactive programs don’t need any user interaction after invocation. Examples include ls, mv and cp.
Interactive, line-based programs are programs that often need interaction from the user during execution. They can write text to standard output, and may request input from the user via standard input. Examples include ed and metasploit.
Text-based user interfaces are a cross between a GUI and a CLI. They are graphical user interfaces running inside a terminal emulator. Examples include nethack and vi. Many (but not all) text-based user interfaces on *nix use either curses or the newer ncurses.
Non-interactive programs get the most attention in this article, while text-based user interfaces are barely covered at all.
Advantages of command-line interfaces
Why use command-line interfaces in the 21st century? Graphical user interfaces were invented decades ago!
Many command-line interfaces provide several advantages over graphical user interfaces, still today. They are mostly popular among power users, programmers and system administrators; partly because many of the advantages apply to their tastes well:
- Ease of automation: Most command-line interfaces can easily be automated using scripts.
- Fast startup times: Most command-line interfaces beat their graphical counterparts several times at startup time
- Easier to use remotely: Maybe it’s just me, but I prefer to remotely control computers via SSH over VNC
- Lower system requirements: The lower system requirements make CLIs useful on embedded systems.
- Higher efficiency: Being able to do a lot by just typing some characters instead of searching in menus increases the efficiency which you are able to do work at
- Keyboard friendly: Grabbing the mouse is a distraction
Disadvantages of command-line interfaces
Having covered the advantages of command-line interfaces, it would be a good idea to also cover the disadvantages of them. The most major disadvantage is that the learning curve is steeper. You will also have to check the manual in many cases, while in a GUI, you can figure out many more things while using the product.
GUIs also have the advantage when it comes to presenting and editing information that is by nature graphical. This includes photo manipulation and watching movies (I have always wanted a program that shows a movie in my terminal by converting it to ASCII art in real-time, that would be sweet).
Try to avoid interactivity
Interactive interfaces are harder to automate than non-interactive user interfaces. The ease of automation is one of the greatest advantages of command-line interfaces, and by making your utility interactive, you give away much of that advantage.
There will be cases when an interactive utility makes more sense than a non-interactive one, but at least don’t make a utility interactive when the advantages are dubious. You shouldn’t create a utility which just asks (interactively) for stuff that could be easily sent to the program as arguments (imagine mv asking you for a source and destination path interactively).
Naming your utility
I would like to stress on the importance of a good name for each command-line utility. This is because a bad name is easy to forget, meaning that users will have to spend more time looking it up.
The name should be short. A long name will be tedious to type, so don’t call you version control program my-awesome-version-control-program. Call it something short, such as avc (Awesome Version Control).
The name should be easy to remember. Don’t call your utility ytzxzy.
Arguments
A lot can be said about the arguments to give to programs. First of all, follow standard practice; single-letter options are prefixed with a hyphen, and multiple of them may follow directly (e.g. -la is the same as -l -a). Multi-letter options start with two hyphens, and each such argument must be separated with spaces. Look at the arguments you give to ls, or cp. Follow the way those work. Examples of commands following non-standard practice include dd, which has been criticized for that, many times.
Continuing on the theme of standard practice, if a similar tool to yours (or a tool in the same category, such as file management) uses some option for some thing, it could be a good idea to copy that behaviour to your program. Look at most *nix file management utilities such as mv, cp and rm. All of these provide a flag called -i, with the same behaviour; asking the user interactively to confirm an action. They also provide a -f flag, to force actions (some of which the computer would think seem stupid, but you know what you are doing while using that option, don’t you?).
Options should be optional. That is part of the meaning of the word, but sometimes it is forgotten. Command-line utilities should be callable with zero, nil and no options at all. Examples include cd, calling it with no arguments returns you to the home directory. Some programs might not make sense to call with no arguments, such as mv, but in many cases, you can find some sensible standard behaviour (remember that it is, however, better to just print an error and exit than to do something stupid the user would never expect to happen (“Oh, you gave rm no options, better remove every file on your disk, just to be sure that the file you wanted to remove gets removed”)).
In the *nix world, there’s a practice that anything after double-hyphens -- should be read as a filename, even if the name contains hyphens (e.g. cmd -- -FILE-). If the arguments list ends with a single-hyphen, input should be read from standard input.
Be careful using many flags whose only difference is the case (e.g. -R and -r), as it will be hard to remember.
Always provide a long form of short arguments. Don’t provide just -i, provide --interactive as well.
Provide --version and --help
There are two options you should always include in your program: --version and --help. The first one should print version information for the program. The second should tell one what the program is for, how to use it and present common, if not all, options.
Reading the input
Make sure your program can read input from pipes, and through file redirection.
If the name of a file is passed as an argument, read the file and use its contents as input. If no such argument was passed, read from standard input until a CTRL+D key sequence is sent.
Silence trumps noise
If a program has nothing of importance to say, then be quiet (the same applies to human beings). When I run mv, I don’t want it to tell me that it moved a file to some other location. After all, isn’t that what I asked it to do? It should come to me as no surprise that that happened, so I don’t need to be told that explicitly. When things you don’t expect to happen do happen, then should you break the silence. Examples include, the file I wanted to move didn’t exist, or I didn’t have permissions to write to the directory I tried to move the file to.
Note that your program don’t need to tell its version or copyright information during each invocation, or print the names of the authors, either. That’s just extra noise, wasting space, bandwidth during remote sessions and possibly making the output harder to automatically process, for instance by sending it to another program through a pipe.
Don’t tell me what the output is, either. One should know what the program they use do. whoami prints the name of the current user, and only the name. If it printed The name of the current user is: x instead of just x, much more work would be involved in extracting just the name.
Most programs provide -v (verbose) options making the program more verbose, and a -q (quiet) option, making the program shut up all together (except possibly for some fatal error). The default behaviour should not be completely silent in every case, but in most cases. Programs that print something should only print what is relevant.
How to ask users for a yes or a no
At times, your programs might need to ask a user for a yes or a no, for different reasons, the most common being confirmations (“do you really want to do this?”). Others could be problems that the computer may offer to fix (“Table USERS doesn’t exist, do you want me to create it for you?”).
While asking for questions requiring either a yes or a no (or a y of an n), you should put (y/n) after the question:
Do you really want to do this (y/n)?
Most programs require you to manually hit return after typing the letter. While this might seem superfluous, most programs do this, and your program should require user to do this as well in order to be consistent and not surprise anyone.
Tell me what kind of input you want, and how you want it
If your program asks for a date, and it doesn’t tell me how I should type that date, I will be confused:
Enter a date:
Tell me the format in which I should input the date, and the confusion is gone:
Enter a date (YYYY-MM-DD):
Likewise, if the program just asks for a length, I would be confused. In kilometers, meters, miles, feet…? When different units for things exist, tell me the unit.
Every program should do one, and only one thing, and do it well
The above heading is one of the most important parts of the Unix philosophy, and it has survived the test of time, being just as solid advice as it was back in the late 1960′s/early 1970′s. In practice, this means that you shouldn’t create a file management program, instead, you should create a program for removing files, another for moving them and another for copying them.
Doing one thing well is partly a side-effect of doing only one thing, which allows greater focus.
“(I have always wanted a program that shows a movie in my terminal by converting it to ASCII art in real-time, that would be sweet).”
You are looking for aalib. There are plugins for popular video players:
http://aa-project.sourceforge.net/aalib/
That seems awesome. Thanks
VLC can do it too:
http://wiki.videolan.org/Documentation:Modules/caca
libcaca does the same thing.
http://caca.zoy.org/wiki/libcaca
VLC also does this iirc.
yeah, libaa or libcaca. to make it short:
mplayer -vo aa example.avi
or
mplayer -vo caca example.avi
for colored output…
Nice article!
Quick nitpick. You misspelled steeper, it says stepper in the text.
Thanks for mentioning it. I have fixed it now.
Another nitpick; I would make the below “doesn’t”.
‘your program don’t need to tell its version’
Movies on your terminal?
http://en.wikipedia.org/wiki/AAlib
“I have always wanted a program that shows a movie in my terminal by converting it to ASCII art in real-time, that would be sweet”
VLC does that with the command line interface.
http://wiki.videolan.org/Video_Output#aa.2C_caca
To watch movies in a shell, you can use mplayer with aalib output. (See e.g. http://oreilly.com/pub/h/4441)
I wouldn’t recommend it, though. I couldn’t read any text and faces and other details disappeared, too, when I tried it.
you are kidding, right?
mplayer -vo aa [movie file]
YMMV, the AAlib has to be built into mplayer for this to work.
Nice article, but I don’t agree with the silence comment. It’s unnerving if an app doesn’t provide any feedback. If all apps obeyed this rule, then you’d be safe in assuming that “no news is good news”, but they’re not, so you’re not (sure). Output doesn’t need to be verbose, but just a line to confirm that what you expected to happen has happened is quite important. Particularly if you’re automating it and logging the output. Reading a log file with nothing in it doesn’t inspire confidence.
If you’re automating it, just check the return code of the program, right?
rc == 0, good
rc == 1, bad
or something along those lines.
If you want the program to be verbose, use the -v or –verbose options. That way both tastes are satisfied.
/B
I think the best way to handle this is to detect whether the standard input stream is connected to a terminal or a pipe.
This is relatively easy to do in any language, and you can use it as the basis for turning on and off extra output from the program.
It’s probably a good idea to have an option to change this behaviour as well though.
I think that’s a very bad idea – it makes it much hard to automate processing of the output, because the output you get when you do a test run on the command line is different than the output you get when running a script.
The only place I’ve seen this detection used helpfully is when detecting whether to send control sequences to colourise output. Such sequences are almost always unhelpful when processing the output, and most users wouldn’t think to include support for them when parsing output from a script, anyway.
Thank you. it would be really nice to use this guidelines
Great article. The absolute most important point is “do one this well”. The tools that stick to that mantra are the ones that get the most use for me. Ever wanted to see your Google Calendar on the command line?
http://code.google.com/p/gcalcli
Great article. I’m going to keep it as a reference.
On a related note, why doesn’t anyone actually read previous comments? Multiple people recommended aalib, yet new commenters kept recommending it. One recommendation is enough.
Maybe comments were pending approval?
I would add the fact that for long options, any non-ambiguous prefix should be accepted. (For instance, “ls –ver” is the same as “ls –version”.) This is a useful feature, but it is missing from some programs.
The issue that you would need to be careful with this sort of feature is automation. If the next version of ls had a verify argument, that would introduce a breaking change in that your command would now be ambiguous.
Nice article, Anders!
Have a look at the “git“ CLI! It’s pure awesomeness.
@Anonymous has a good point with the -v/–verbose option.
Every CLI should have a good man page (e.g. “man mv“) providing context and examples.
Reading from STDIN should be the same as supporting pipes – at least in python it is. See this simple echo program:
import sys; print sys.stdin.read();
Also being interactive can be quite useful if you are used to writing (BASH) here documents:
mysql -p <<EOF
SHOW databases;
EOF
But I absolutely agree with your point: CLIs should support arguments as well as STDIN (and pipes).
Cheers,
Felix
I think you missed an important point about discovery, both of commands and arguments; meaning how do you find the right command/program do to something and how do you make it do what you want. This is an area where GUIs excel, but CLIs can a more than adequate job: for instance bash command completion and Emacs function completion.
I’d put a “and do it well AND be consistent with the rest of the system.”
When one program ask for date in DDMMYY and other in DD/MM/YYYY, there will be trouble. The same with options/switches/whatever. Some use -, some / and some nothing at all.
Provide “-h” also, not only “–help”.
Provide a good man page.
I disagree with the importance of a short name: with tab completion I see as more valuable a meaningful and mnemonically easy name.
You should have named your article “Designing command-line applications for Unix-based systems” or something like that, because the rules you said are not carved in stone and there are very reasonable CLIs, that do exactly the opposite.
Take VMS for example. It’s CLI is very mighty and you can accomplish practically anything on it, it’s just not a cheap junk OS like DOS and it’s primitive CLI. Yet, it disobeyes practically anything you just said in your article.
- Arguments don’t start with – or –, they start with /.
- There is no need to distinguish between long and short versions, because you can automatically shorten anything (program names as well as it’s parameters) as long as there is no ambiguousness.
- there is no need for a space between parameters, you can just write DIR/DATE/OWN and it will print the contents of the current directory including date and owner
- “one program does exactly one thing” is completely not there. The SET utility does anything from managing your own user account (password), managing the system itself (time) to even more obscure things like VMS’ way of doing “telnet” (SET HOST target).
- “avoid interactivity” is also not there. e.g. COPY takes source and destination as arguments, but when you do not give them, it will ask you for the required information. So you get both scriptability as well as user friendlyness.
Of course, almost nobody is using OpenVMS anymore and the Unix-way of doing CLI is quasi-standard, it’s not the only way to do and at least in some aspects not the best way to do, either. (imho)
DCL (Digitial Command Language, for non VMS users) had a HUGE overhead.
To add a new command required compiling a command definition into a binary file, which was then merged with the existing command tree (stored in memory) every time the user logged in.
The command parser had its own memory area, not available to the user process – you had to use that special library to retrieve the parsed command parameters.
Holding the entire command tree (all possible commands, all possible options, all possible parameters) requiree a huge overhead in memory.
It also limits what can be interpreted as a command.
The UNIX way, has always been more flexible, allows an unlimited number of commands, and trivial access to options (the argc/argv count and list).
I didn’t want to claim, that DCL is superior to the CLI in Unix, I just wanted to illustrate, that there are completely different concepts, that still can make a lot of sense towards the user.
Eric Raymond’s book “Art of Unix Programming” deserves a mention here. It covers many more aspects of the Unix design philosophy.
If possible allow for multiple files to specified (again see cp or mv for example). If your program asks for yes or no also have a “-y” option to automatically answer these with yes.
“-” usually means stdin or stdout depending on whether it is specified as input or output.
Also ensure to print errors/debug messages to stderr and set a proper return code.
It helps to use an option parsing library for your programming language. For C: popt or GLib’s GOption.
http://developer.gnome.org/glib/2.28/glib-Commandline-option-parser.html
Excellent article, thanks!
To round it up, I’d like to mention as a very good example the RabbitMQ management plugin.
It provides three interfaces in one: a Web UI, a REST API, and a CLI. The CLI (a python script) can be downloaded from the Web UI, and is so user friendly. It even has a switch that produces a bash completion script you can store under /etc/bash_completion.d, so you can have tab autocompletion for it.
Fascinating piece of software.
One more movie-in-ascii: there’s a sample program that does this as a way to show how to use now-depreciated Quicktime APIs: http://developer.apple.com/library/mac/#samplecode/ASCIIMoviePlayerSample/Introduction/Intro.html
“Grabbing the mouse is a distraction”. No, it isn’t. In face, recalling a keyboard command *is* a distraction, therefore your brain doesn’t recall it. When you “grab the mouse”, you don’t need to make a cognitive action, and your brain doesn’t glaze over it. There are researches that indicate that, even with the need to move your hand to the mouse, mouse action is faster.
Now, naturally, there are actions that the keyboard is way more suitable for than the mouse, but this is not what you hint, and by making this claim, you sadly undermine the UI expertise needed when you present such an article.
“In the *nix world, there’s a practice that anything after double-hyphens — should be read as a filename, even if the name contains hyphens (e.g. cmd — -FILE-). If the arguments list ends with a single-hyphen, input should be read from standard input.”
Your note about ‘–’ (dash dash) is well intended but slightly incorrect. — is a marker which means “stop interpreting the arguments as options; all subsequent elements of argv[] are operands to the command.” It really has nothing to do with file names.
A good example of using — is the following. Your task is to look through a file for occurrences of “-lollipop”. If you do this:
grep -lollipop myfile.txt
You will get something like:
grep: illegal option — p
Usage: grep [OPTION]… PATTERN [FILE]…
Try `grep –help’ for more information.
This is because grep thinks you mean, semantically: “-l -o -l -l -i -p -o -p”.
What you want is:
grep — -lollipop myfile.txt
The — causes the getopt(3c) parser to stop, and to regard -lollipop as an operand.
firstly, I have read all the above comments before posting my own comment here.
I would like to also bring into everyone’s attention this tool called ‘expect’ at http://expect.nist.gov/. This tool will handle the automation of command line apps that require interactive input from the use.
Thank you, ladies and gentlemen.
Why do you use the UK English spelling of behaviour, but the US English spelling of meters (instead of metres), and kilometers (instead of kilometres)?
@Rohan Dhruva:
Probably because he’s not a native English speakers, since he’s Finnish, and he doesn’t bother so write pure American or British English? Like 99% of us, non-native English speakers
@author:
The ASCII art movie part was a bit of blunder, since these things have been around for >10 years. And they’re fun, but in now way usable.
Decent article, especially the measurement unit is important. Who hasn’t tried to give a “duration” parameter to a command, not knowing the unit, i.e.:
sleep
TIME – seconds? milliseconds? hours?
N.B. I’m not complaining about the actual sleep command, which is documented, but the billion of tools having such undocumented parameters.
Broaden your perspective beyond *nix.
Cryptic commands with a non-uniform naming convention and “switches” are archaic, a symptom of an environment which was built piecemeal rather than designed.
Check out the command line environment used by the IBM i.
http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=/rzal2/rzal2commands.htm
Yes, a -v or –verbose option is my preference too. Run silent by default, but provide the ability to print out details for those that want it.
Whenever you’re asking the user to type something like this:
Do you really want to do this (y/n)?
You should do it like this:
Do you really want to do this (y/N)?
The capital N makes the default clear, so if the defaults are what you want, you can just hit enter.
Hi there,
Interesting article!
I can see a lot of people have pointed you at various film -> ascii converters, but it might also interest you to know that back in 2006 the World Cup was streamed LIVE in ASCII via telnet!
The website for it is here: http://www.ascii-wm.net/doc.php
I remember watching it at work!
Hi,
Sorry I write you via comments. But I could not find contact e-mail or feedback form on your site.
We are looking for new advertisement platforms and we are interested in your site http://www.antoarts.com.
Is it possible to place banner on your site on a fee basis?
Please, contact us at e-mail.
Best regards,
Sacha Charles
P.S: delete this comment.
no they are just based on things like the clubs recent form, injuries, previous matches, wether there a defensive team like brum or a high scoring team like arsenal.
—————————————
http://prowinningtips.com/img/banner0215.jpg
university of iowa college of pharmacy http://sundrugstore.net/products/pilocarpine.htm cvs pharmacy chicago