DEV Community

Ali Sherief
Ali Sherief

Posted on • Edited on

Passing command-line options to bash scripts

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:

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
Enter fullscreen mode Exit fullscreen mode

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}.

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

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.

Top comments (2)

Collapse
 
taikedz profile image
Tai Kedzierski • Edited

There is so much wrong here, you obviously have not checked your code at all.

  • You forgot to close the case block, which is a basic syntax error
  • Your usage of getopts is completely wrong - this is the correct usage
  • --longoptions does not exist. The getopts built-in command does not take such options. You may have used some external getopts command (which you might find with which getopts) but your example does not apply to normal systems
  • And my pet peeve, you're not quoting any of your variables, opening up to bugs galore.

Don't post code you haven't checkd, for pity's sake.

The bash scripting ecosystem is rife enough with bad examples and bad code, we really don't need any more atrocious and unchecked posts about it.

Collapse
 
zenulabidin profile image
Ali Sherief • Edited

I do admit I extracted the first example from a larger script of mine, and the second example came from the Stack Overflow link you linked.

I'll fix the problems you pointed out, thanks for letting me know about them. It's why I have the disclaimer at the end.

Regarding the --longoptions comment though, GNU getopt does have such an option. It's the version I used in the first example.