How to read command line arguments in a bash script

Last updated on March 1, 2021 by Dan Nanni

A bash script rarely runs standalone. Rather, you often pass one or more arguments to the script from the command line to modify its run-time behavior. The script then reads the supplied arguments, parse and process them accordingly. In more advanced cases, you may want to pass command-line options to your script (e.g., "-h" or "-f my.txt") to optinally change the default settings of the script. In this tutorial let's find out how you can pass command-line arguments and how you can handle command-line options in a bash shell script.

Method One: Read Command-Line Argument by Their Positions

The command-line arguments that are passed to a bash script are called positional parameters because how these arguments are referenced inside a script depends on the position of the arguments in the command line. More specifically, the command-line arguments are referenced by digits that are incrementing in the order of appearance in the command line (e.g., $1 for the first argument, $2 for the second argument, ..., $N for the N-th argument). When you access the N-th argument where N contains more than one digits (e.g., the 10-th argument), you need to surround N with {} (e.g., ${10}). $0 is a special bash parameter that stores the name of a bash script.

The following bash script example checks if the first (second) argument exists, and prints its value if it does.

if [ -z $1 ]; then
    echo "The first argument is not set"
else
    echo "The first argument: $1"
fi

if [ ! -z $2 ]; then
    echo "The second argument: $2"
else
    echo "The second argument is not set"
fi

If you want to check the total number of arguments passed to a bash script, you can use a special bash parameter called $#.

if [ $# -ne 3 ]; then
    echo "You need to provide three arguments"
    exit 1
fi
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
    echo "Number of arguments must be between 1 and 2"
    exit 1
fi

Method Two: Read Command-Line Argument with for Loop

Another way to check command-line arguments in a bash script is by referencing a special bash parameter called $@, which expands to a list of supplied command-line arguments. Since $@ is an iterable list, you can use for loop to read each argument in the list. For example:

pos=1
for arg in "$@"; do
  echo "$pos-th argument : $arg"
  (( pos += 1 ))
done

Note that you need to surround $@ with quotes in case any command-line argument contains whitespaces. When you run this script, you will see the following output.

$ ./script.sh 100 linux "hi there" 
1-th argument : 100
2-th argument : linux
3-th argument : hi there

Method Three: Read Command-Line Options with getopts

The previous two methods access ordinary command-line arguments. In some cases, however, you may want to handle command-line options in a shell script, which may or may not be specified from the command line. The command-line options are typically single letters preceded by a single hyphen. Some options are used standalone (e.g., -h), while other options and their arguments are provided together (e.g., -f file.txt). When a shell script reads command-line options, what matters is not the position of an option in the command line, but rather what option flag is used.

Bash provides a built-in function called getopts to handle such command-line options. It's much easier to learn getopts with examples.

while getopts 'hi:f:' OPTION; do
    case "$OPTION" in
        h)
            echo "Option h is given"
            ;;
        i)
            iarg="$OPTARG"
            echo "Argument provided with option i: $iarg"
            ;;
        f)
            farg="$OPTARG"
            echo "Argument provided with option f: $farg"
            ;;
        *)
            echo "Usage: $0 [-h] [-i value] [-f filename]" >&2
            exit 1
        ;;
    esac
done

In the example script above, the while loop iterates each of all possible command-line options and processes them. The string that follows getopts indicates possible option flags accepted by this script. Our example accepts "-h", "-i" and "-f" flags. The ":" sign after an option flag (e.g., "-i" and "-f") indicates that the option requires an argument. For those options that take an argument, an actual argument value is passed as $OPTARG. The case statement with "*" is a catch-all statement which is matched when any unsupported command-line option is entered.

Note that the entire while loop can be skipped if no command-line option is supplied from the command line.

Method Four: Read Both Command-Line Options and Ordinary Command-Line Arguments

In the final scenario, let's say a given script takes (optional) command-line options as well as (required) positional parameters. The following example script handles such a scenario, which combines the example scripts from Method One and Method Three.

while getopts 'hi:f:' OPTION; do
    case "$OPTION" in
        h)
            echo "Option h is given"
            ;;
        i)
            iarg="$OPTARG"
            echo "Argument provided with option i: $iarg"
            ;;
        f)
            farg="$OPTARG"
            echo "Argument provided with option f: $farg"
            ;;
        *)
            echo "Usage: $0 [-h] [-i value] [-f filename]" >&2
            exit 1
        ;;
    esac
done

shift "$(($OPTIND -1))"

if [ -z $1 ]; then
    echo "The first argument is not set"
else
    echo "The first argument: $1"
fi

if [ ! -z $2 ]; then
    echo "The second argument: $2"
else
    echo "The second argument is not set"
fi

One important statement in the above example is the following line.

shift "$(($OPTIND - 1))"

$OPTIND is a special bash variable which is set to 1 upon launch of this script. As each option field is parsed, $OPTIND is incremented by one. shift is a built-in function which moves positional parameters. When shift N is called, it discards the first-N positional parameters from the command line. Thus the above statement discards all command-line options (if any) that are processed, leaving only subsequent ordinary command-line arguments, so that we can reference them using $1, $2, etc.

For example, when you run the script with the following arguments:

$ ./script -f my.txt argument1 argument2

You will see this output as expected:

Argument provided with option f: my.txt
The first argument: argument1
The second argument: argument2

If you find this tutorial helpful, I recommend you check out the series of bash shell scripting tutorials provided by Xmodulo.

Support Xmodulo

This website is made possible by minimal ads and your gracious donation via PayPal or credit card

Please note that this article is published by Xmodulo.com under a Creative Commons Attribution-ShareAlike 3.0 Unported License. If you would like to use the whole or any part of this article, you need to cite this web page at Xmodulo.com as the original source.

Xmodulo © 2021 ‒ AboutWrite for Us ‒ Feed ‒ Powered by DigitalOcean