Command line arguments order

argumentscommand line

Someone once told me there was a standard when it comes to parsing options on the command line. Something like:

./script.sh [options/flags] [command or file]

I understand that when parsing a shell script this makes life easier since you can shift through the flags and anything left can be accessed by $@ or $*, but is there an actual written standard?

Most programmes I've looked at follow this standard, but there are some exceptions, eg ls where ls -l /path, ls /path -l and ls /path -l /path2 are all acceptable.

Best Answer

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.

Related Question