Argument handling
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 | tac
is two sets of single words,./dwim.bash
andtac
(which reverses lines), because|
separates two commandsExamples of multiple words include:
./dwim.bash ./my documents
is three words,./dwim.bash
,./my
anddocuments
, because none of the spaces are escaped or quoted
./dwim.bash "./$user"' 'documents
is two words, because Bash supports mixing quoted and unquoted strings in the same word
./dwim.bash ./$user documents
is at least three words, because word splitting happens after expanding variables, and$user
might 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
${10}
.
$0
is 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 grep
, $1
, which is foo.*bar
, and $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 GNUfind
manual 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:
#!/usr/bin/env bash
This page is a preview of The newline Guide to Bash Scripting