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.