Aim of this section: Handle arguments in a simple, flexible and recognizable manner.
An “argument” is a word passed to a command. Most commands require arguments to do something useful, such as deploying to a specific environment or processing some files.
A “word” in Bash is a strange concept, only vaguely related to natural language words. A Bash word is any sequence of bytes (excluding NUL) bracketed on either side by the start of a simple command, an unescaped, unquoted space, tab or newline character, or the end of a simple command. Examples of single words include:
./dwim.bash, since it is bracketed by the start and end of the command
./my\ documents/, since the space character is escaped with a backslash
'./my documents', since the string is quoted
./dwim.bash | tacis two sets of single words,
tac(which reverses lines), because
|separates two commands
Examples of multiple words include:
./dwim.bash ./my documentsis three words,
documents, because none of the spaces are escaped or quoted
./dwim.bash "./$user"' 'documentsis two words, because Bash supports mixing quoted and unquoted strings in the same word
./dwim.bash ./$user documentsis at least three words, because word splitting happens after expanding variables, and
$usermight contain any number of words, including zero
Arguments are stored — in order — in the parameter array
$@, and can be accessed individually as
$1 (the first parameter) onwards. Basically we can think of the parameter as the name, key or 1–based index, and the argument as the value.
You need to use curly brackets to refer to the tenth and subsequent arguments, as in
$0is the command name itself, and is not considered a parameter or part of the parameter array.
So we can break a command like
grep 'foo.*bar' './some file.txt' > ./output.txt into
$0, which is
$1, which is
$2, which is
./some file.txt. The redirection is not part of the arguments.
An “option” is an argument which modifies the behavior of the program. Options can be either “flags” such as
--verbose, which toggle a boolean, or a key/value pair such as
--configuration=./my.conf. Option arguments may be followed by “non–option arguments”, which is the input the command will work with, such as a file name. An optional separator of
-- is sometimes used to keep options and non–options apart. This allows specifying non–option arguments starting with hyphens as in
grep foo -- -test.txt, although as we’ll see this is an antipattern with a simple workaround.
Putting options before arguments when writing commands is a good habit to get into.
some-command /some/path --some-option will work with some commands, but argument parsing is implemented in countless different ways, and some programs will do unexpected things (or just fail) if we mix these together.
Hold on though, what about
find? Doesn’t it put the paths to search for in the middle and the options (like
-type d) at the end? It’s confusing, but the synopsis in the GNU
findmanual page explains this: it starts with a handful of rarely–used options, followed by the “starting points,” and the rest of the arguments all make up an expression which filters and manipulates the files within the starting points. The keywords in the expression just happen to look like options.
The following script shows how you might set up argument handling: