6

Consider the following command-line program manage-id. It does these things:

manage-id list                       (list all usernames and user-ids)
manage-id show  <username>           (shows username's id)
manage-id clear <username>           (erases username's id) 
manage-id set   <username> <user-id> (sets usernames id)
manage-id find  <string>             (list usernames whose id contains <string>)

The above is one way to design the user interface. Here is another:

manage-id --action list
manage-id --action show  --username <username>
manage-id --action clear --username <username>
manage-id --action set   --username <username> --id <user-id>
manage-id --action find  --search <string>

The first is a "positional argument design" and the second a "command-line option design".

I tend to prefer the "command-line option design" for a few reasons:

  • the arguments can be presented in any order
  • the option names are self-documenting
  • removes ambiguity about role of argument (e.g., in the two commands manage-id show johndoe and manage-id find john, the second argument plays different roles).

On the other hand, the "command-line option design" uses "options" that are not really optional.

My question is this: Is there a recommended (and widely-followed) style choice that prefers one of these two styles over the other for Linux command-line programs?

1
  • 5
    I would strongly recommend only using the command-line option design for optional parameters. – David Arno Feb 19 '18 at 16:54
18

Your question is a false dichotomy.

Most places I’ve seen use both. Position based parameters for required parameters, and options based parameters for optional parameters. Kinda makes sense, yeah?

3

The style is ultimately dictated by the complexity of the control that is needed. If you have but one option it is OK to use positional. If you have two it starts to get debatable what the most convenient way to go is. And for whom? For you, the programmer? Or for the user?

There is another level: sub commands. Look at the Git command line reference for instance.

If you make a lot of command line utilities, you will develop some sort of argument parser that makes it easy to feed complex argument collections into your program and access them easily from code. And then you will support a style of parsing arguments that is universal across your tool set.

Note you can have default values for named arguments just the same. So they can be optional, that is up to you.

Be sure to support a usage page. If one enters an invalid command, display a page that documents the options.

1

I don't know if this SO answer is linguistically authoritative, but it does explicitly differentiate between:

arguments -- any part of a command
options -- information that modifies the behavior of the command
and
parameters -- extra information (not sure how this differs from an option)

1

Historically, and as other users have answered, programs have used positional arguments for mandatory arguments and command-line options for optional stuff.

Ancient programs and commands like cp show clearly in their syntax that SOURCE and DEST are positional and mandatory, whereas all the rest are options:

cp [OPTION]... [-T] SOURCE DEST

Later as complexity increased, some CLI programs began to use two-hyphen options with longer names as they ran out of single letters (see this useful answer in a sister site), with existing one hyphen switches gaining a longer, self documenting alternative:

-f, --force

But I understand that your program (or your example) is one of those complex CLI apps that have actions/commands with their own parameters. In this kind of program, positional arguments behave like "default" command line options when no option is used.

One could see --action in your second example as the default option that is omitted in the first example where no option was used, actually enforcing a mandatory option.

Also --username can be seen as the default sub-option for --action. The parser expands omitted options as being the default ones.

I believe some CLI programs behave like that (postgres comes to mind besides git).

The more complex the basic behavior of a CLI program is, the more it relies on named, non-positional command-line options. For example, git, has a complex interface and no option is positional, although one could consider the big "options section" comes first then the big "command and args" section.

git [--version] [--help] [-C <path>] [-c <name>=<value>]
    [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
    [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
    [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
    [--super-prefix=<path>]
    <command> [<args>]

The example you give is a git-style app, with a set of commands passed as switches that themselves have several options.

So to answer your question I dare to say nowadays, newer CLI apps and those that have complex behavior tend to rely heavily on named non-positional options (some, like git use "commands" that don't use hyphens).

I design and program complex command-line apps and I use named options with some of them being mandatory and used as default when users don't specifically writes them, for example:

$ dbsearch production

looks like there's a single mandatory positional parameter but what my parser really does is that it assumes some mandatory options

$ dbsearch -l -a production

and its equivalent

$ dbsearch --list --search_argument production

Some of the following is almost always considered part of a good CLI interface:

  • command line completion (to help with a complete set of options)

  • man page

  • useful "usage messages" that "guide you" like git's:

    On branch master
     Changes to be committed:
     (use "git reset HEAD <file>..." to unstage)
    
    modified: hello.py
    
     Changes not staged for commit:
     (use "git add <file>..." to update what will be committed)
     (use "git checkout -- <file>..." to discard changes in working directory)
    
    modified: main.py
    
     Untracked files:
     (use "git add <file>..." to include in what will be committed)
    
    hello.pyc
    

These messages tell you what to do next or what is missing.

Additionally some CLI utilities, like for example some of Oracle's utilities, can as an alternative accept a "parfile" in which all parameters and values can be saved in a key-value file and passed as an argument instead of having a very long series of options and arguments, I particularly use this in my designs.

-1

"Positional argument design" has one (and major) reason: parser simplicity. This is why most of MS-DOS commands use that way - it's cheap! As soon as you're going into detailed parameters flow, you're out of that luck. So, you have to use key/value approach which you call "option design".

Yes, "option design" makes command-line parsing much more complex. And in turn it gives you more freedom as well (this is very useful once you generate that command-line arguments by sophisticated logic and would like to avoid stateful design).

Same time, as mentioned by Paul, there is no restriction for using both approaches in the same construct (so basic part is positional, and tail one is option-based).

3
  • 1
    You haven't answered the question. – whatsisname Feb 19 '18 at 22:35
  • Answer: there is no such recommended style choice. Just consider your goals according to the cases described above. – Yury Schkatula Feb 20 '18 at 9:25
  • MS DOS commands, just like Unix commands, consistently use a mixture of positional and optional arguments. There is a recommended style choice: do what the majority of commands do and mix the two styles. – David Arno Feb 20 '18 at 10:38
-1

My question is this: Is there a recommended (and widely-followed) style choice that prefers one of these two styles over the other for Linux command-line programs?

I don't know if there is any official document suggesting it, but basically all of the standard unix tools use positional arguments for non-optional arguments.

Not the answer you're looking for? Browse other questions tagged or ask your own question.