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
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
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)
There is so much wrong here, you obviously have not checked your code at all.
case
block, which is a basic syntax error--longoptions
does not exist. Thegetopts
built-in command does not take such options. You may have used some externalgetopts
command (which you might find withwhich getopts
) but your example does not apply to normal systemsDon'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.
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.