Passing command-line options to bash scripts
Tags: linux, bash
Published: 2021-01-11 05:02:45

Have you ever wondered how bash scripts process options? Have you ever wanted to make a script that takes options but you didn't know how to? In this guide I will show you how you can use the `getopts` or `getopt` commands to parse options passed to the user. You will also be able to take positional arguments while also supporting options.

## Option parsing

Option parsing is done by means of the built-in `getopts` command if you only need to parse short options, or by using an external command called GNU getopt which also lets you take long options.

These commands take an string of a form like `abc:de:`, which corresponds to allowing the options `-a -b -c VALUE -d -e VALUE`. They also support the `--` end of options symbol and the positional arguments that follow it. 

In addition to the above, you can also supply `--long-options foo,bar:,baz:` exclusively for GNU getopt (which I will simply call `getopt` from now on) and this corresponds to allowing `--foo --bar VALUE --baz VALUE`.

Neither of these commands actually processes the options for you, they only check the options list against the options the end user provided and make sure that they only gave valid options.

How to actually parse the options after that differs for each program. `getopt` will print the option string to standard output, which you can capture with `$()` notation or backticks. Let the script exit if `getopt`/`getopts` terminated from finding invalid options. Then you replace the options of your own program (located at `$@`) with the output variable. Then use a whole loop to shift options to the left so you can always get the current option at $1, and arguments it may have as $2. There will always be a `--` returned, so processing that will terminate the while loop.

Inside the whole loop you have `case` statements for each option and you set a bash variable for each one accordingly. This is also the place to check that arguments are of the type and range you need the to be in e.g. a positive number. At this point you can also fail with a usage statement if any options have invalid arguments (the options themselves were already checked with getopt).

The whole code would look like this:

```sh
usage() {
    echo "Simple login app."
    echo "Usage: $0 -u USERNAME -p PASSWORD"
}

OPTS=$(getopt --options u:p: --longoptions 'username:,password:' -n simploginapp -- $@)

eval set -- "$OPTS"

while [ -n "$@" ]; do
    case "$1" in
        -u | --username)
            username="$2"
             shift 2
            ;;
        -p | --password)
            password="$2"
             shift 2
            ;;
        --)
            break
            ;;
        *)
            echo "Unrecognized option '$1'"
            usage
            ;;
    esac
done
```

Some programs have a --help option that prints a detailed help message of the available options, that is specified in the same way.

I recommend using `getopt` for validating the options, but if you need to use `getopts`, perhaps because GNU getopt isn't installed on the system you're running the script on, you'd similarly use a whole loop and case statements, but you're directly looping over the return value of `getopts` instead, and and option argument is stored in a variable called ${OPTARG}.

```sh
while getopts "u:p:" arg; do
  case "${arg}" in
    u)
      username="${OPTARG}"
      ;;
    p)
      password="${OPTARG}"
      ;;
    *)
      echo "Unrecognized option '${arg}'"
      ;;
    eaac
done
```

## And we're done

Congratulations, now you know not just one, but two ways how to pass command line options to your shell scripts. I hope you make some great things with it.

If you see any errors in this post, please let me know so I can correct them.