Jump to content

Bash Shell Scripting/Positional Parameters

From Wikibooks, open books for an open world

In most of the above commands — both those that run a built-in command, and those that use an external program — we have supplied one or more arguments, that indicate what the command should operate on. For example, when we invoke the common Unix utility mkdir ("make directory") to create a new directory, we invoke it with a command like this one:

mkdir tmp

where tmp is the name of the new directory to create.

And as we have seen, Bash scripts are themselves programs that can be run. So it goes without saying that they, too, can take arguments. These arguments are available to the program as its positional parameters. Earlier, we saw that variables are one kind of parameter. Positional parameters are very similar, but are identified by numbers rather than by names. For example, $1 (or ${1}) expands to the script's first argument. So, suppose we want to create a simple script called mkfile.sh that takes two arguments — a filename, and a line of text — and creates the specified file with the specified text. We can write it as follows:

#!/bin/bash
echo "$2" > "$1"

(Notice the line #!/bin/bash at the very beginning of the file; we covered that line at Basic commands. When you run this code, that line wil guarantee that it will be interpreted by the Bash shell, even if you are running from another program or your computer has a non-standard configuration.)

After making it executable by running chmod +x mkfile.sh, we can run it as follows:

./mkfile.sh file-to-create.txt 'line to put in file'

We can also refer to all of the arguments at once by using $@, which expands to all of the positional parameters, in order. When wrapped in double-quotes, as "$@", each argument becomes a separate word. (Note: the alternative $* is perhaps more common, but "$*" becomes a single word, with spaces in between the original parameters. "$@" is almost always preferable to either $@ or $*, which allow an argument to become split into multiple words if it contains whitespace, and to "$*", which combines multiple arguments into a single word.) This is often useful in concert with the built-in command shift, which removes the first positional parameter, such that $2 becomes $1, $3 becomes $2, and so on. For example, if we change mkfile.sh as follows:

#!/bin/bash
file="$1" # save the first argument as "$file"
shift # drop the first argument from "$@"
echo "$@" > "$file" # write the remaining arguments to "$file"

then we can run it as follows:

./mkfile.sh file-to-create.txt line to put in file

and all of the arguments, except the filename, will be written to the file.

The number of positional parameters is available as $#; for example, if $# is 3, then the positional parameters are $1, $2, and $3.

Note that positional parameters beyond $9 require the curly braces; should you need to refer to the tenth argument, for example, you must write ${10} rather than $10. (The latter would be interpreted as ${1}0.) That said, it is not usually a good idea to have so many arguments with specific meanings, since it is hard for users to keep track of them. If you find yourself specifically referring to your script's tenth argument, it may be worth re-evaluating your approach.

If you have some experience with Bash, or with Unix utilities, you've most likely noticed that many commands can take various "options", indicated with a leading hyphen -, in addition to their regular arguments. For example, rm "$filename" deletes an individual file, while rm -r "$dirname" deletes an entire directory with all of its contents. (The -r is short for "recursive": the command "recursively" deletes an entire tree of directories.) These options are actually just arguments. In rm "$filename", there is just one argument ("$filename"), while in rm -r "$dirname" there are two (-r and "$dirname"). In a sense, there is nothing inherently special about these arguments, but this notation for options is so widespread as to be considered standard; many or most of the Bash built-in commands can accept various options, and later we will see various techniques for supporting options as arguments to our Bash scripts.