Historical Basics

Updated at 2022-05-08 09:25

This note contains basics of historical Unix command line syntax. Most of this applies even to this day if nothing else is stated. This explains the "why" behind some of modern command line conventions.

If a program is given unnamed arguments, those were filenames to be used as input.

cat <<EOF > greeting.txt
hello world!
cat <<EOF > farewell.txt
bye you!

wc greeting.txt
# 1  2 13 greeting.txt

wc farewell.txt
# 1 2 9 farewell.txt

wc *.txt
# 1  2  9 farewell.txt
# 1  2 13 greeting.txt
# 2  4 22 total

The program output would be written to the standard output. This allows chaining or "piping" of commands.

If no filenames were given, it would read the standard input. This is common to this day.

# will open a text prompt so you can write your input

cat *.txt | wc
# 2  4  22
# but now has no knowledge of the filenames as the data was piped through standard input

Single Dash Options

Originally, options always started with a single dash and be a single character. This made parsing simple. The option value could be right after the character or be the next argument.

cat *.txt | sort -r
# hello world!
# bye you!

cat *.txt | sort -k1
# bye you!
# hello world!

cat *.txt | sort -k2
# hello world!
# bye you!

cat *.txt | sort -k 2
# hello world!
# bye you!

Multiple single character options could be "packed". For example, -a -b -c could be shortened to foo -abc.

cat *.txt | sort -k2r
# bye you!
# hello world!

Soon it was realized longer option names will be required. For example, if you have display and depth which would logically both be -d, and you will eventually run out of characters in a more complex program.

But this caused:

  • Packing became situational as -depth could also mean -d, -e, -p, -t, -h, thus is became responsibility of the program to parse and communicate usage correctly.
  • You can't uniformly parse option values without knowing the option names if there is no space between the option name and value e.g. -d1, -d 1 and -depth 1 are valid but -depth1 is incorrect (if the 1 is value and not part of the name.)

Double Dash Options

Later, double dash -- became to communicate a long option. Initially the long option marker was + but that is rare to find nowadays.

--color auto

Around the same time, equals sign = became the optional way to specify value. This made creating universal parser much simpler.


Then order of options and other arguments became less strict. Before this, options must've been defined before other arguments (i.e. filenames.)

grep -xi *.txt --regexp=foo --regexp bar
# obs. the beginning is expanded to `-x -i`

Double Dash Termination

Empty double dash -- became to signify "no further options". It was realized that some situations were impossible without a way to terminate options manually.

# if you want to delete a file name `-f`
rm -- -f

# also useful if you run a program that runs another program with options
cargo run -- --version


Subcommands were a simplification of exclusive options. Exclusively checking for e.g. app --encrypt or app --decrypt was a bit tedious to both use and validate by the programmer. Using first unnamed arguments as "subcommands" instead of filenames became the norm for early version control software and has since become a standard practice for more complex command line tools.

git --help         # show help for the whole `git`
git status --help  # show help for `status` subcommand
git add --help     # show help for `add` subcommand