Command line arguments order

The POSIX Base Definitions has a section on "Utility Conventions" which applies to the POSIX base utilities.

The standard getopts utility and the getopt() system interface ("C function") adheres to the guidelines (further down on the page linked to above) when parsing the command line in a shell script or C program. Specifically, for getopts (as an example):

When the end of options is encountered, the getopts utility shall exit with a return value greater than zero; the shell variable OPTIND shall be set to the index of the first operand, or the value "$#" +1 if there are no operands; the name variable shall be set to the character. Any of the following shall identify the end of options: the first -- argument that is not an option-argument, finding an argument that is not an option-argument and does not begin with a -, or encountering an error.

What this basically says is that options shall come first, and then operands (your "command or file").

Doing it any other way would render using getopts or getopt() impossible, and would additionally likely confuse users used to the POSIX way of specifying options and operands for a command.

Note that the abovementioned standard only applies to POSIX utilities, but as such it sets a precedence for Unix utilities in general. Non-standard Unix utilities can choose to follow or to break this, obviously.

For example, the GNU coreutils, even though they implement the standard utilities, allows for things like

$ ls Documents/ -l

if the POSIXLY_CORRECT environment variable is not set, whereas the BSD version of the same utilities do not.

This has the consequence that the following works as expected (if you expect POSIX behaviour, that is) on a BSD system:

$ touch -- 'test' '-l'

$ ls -l test -l
-rw-r--r--  1 kk  kk  0 Jan 11 16:44 -l  
-rw-r--r--  1 kk  kk  0 Jan 11 16:44 test

But on a GNU coreutils system, you get

$ ls -l test -l
-rw-r--r-- 1 kk kk 0 Jan 11 16:44 test

However:

$ env POSIXLY_CORRECT=1 ls -l test -l

and

$ ls -l -- test -l

will do the "right" thing on a GNU system too.


tl;dr

  • There is a standard command line format.
  • It requires options before arguments.
  • That part of the standard is widely ignored.

an example

All three of the option parsing libraries I have worked with in python default to allowing interspersed arguments and provide an illustration of what is common:

  1. The now deprecated python standard library module optparse allows:

    OptionParser.enable_interspersed_args()
    

    Set parsing to not stop on the first non-option, allowing interspersing switches with command arguments. This is the default behavior.

  2. The current python standard library module argparse allows (and only allows) interspersed arguments.

  3. click, the library I am currently favoring, allows interspersed arguments to be disabled:

    click.Context(allow_interspersed_args=False)
    

    But discusses this possibility in the advanced section under trouble shooting unknown args to sub-commands.