Argument Parsing in Shell Scripts
So, say you are writing a shell script (sh or bash) that needs to take arguments like so:
./script.sh start
./script.sh -v start
./script.sh -c foo.conf start
./script.sh -vc foo.conf start
This took me a bit of doing. First, I tried getopt but apparently all that does is disambiguate -v from -c. That, and the FreeBSD man page claims to have improved the example by making it completely inscrutable.
Eventually I found a reference that explained how to use getopts, which worked a lot better, but all the examples I could find didn’t tell you how to reset $@.
Eventually, I figured out that that last bit is accomplished with shift.
Here’s example code:
#!/bin/sh
#
conf=default.conf
while getopts c:hv o
do case "$o" in
c) conf="$OPTARG";;
h) echo "Usage: $0 -hv -c <config> [start|stop|restart|status]" && exit 1;;
v) verbose="yes";;
\?) echo "Usage: $0 -hv -c <config> [start|stop|restart|status]" && exit 1;;
esac
done
# Reset $@
shift `echo $OPTIND-1 | bc`
echo "conf: $conf"
echo "command: $1"
Notes:
- The while loop iterates
$othrough$@where it conforms to the getopts configuration. - getopts c:hv indicates that -h and -v are boolean arguments, and -c is followed by a string argument.
- $OPTIND is set at the end of getopt, and indicates how many arguments through $@ it parsed.
- Oh, and the shift operation resets
$@by $OPTIND-1, so that$1ends up pointing at the start command, or whatever. - If you’re programming in ksh, you can just set
$OPTIND-1. But if you’re programming in ksh you are far beyond needing to check my blog for argument parsing.
Responses
Andrew Ho
My standard sh command line parsing loop looks like this:
while [ $# != 0 ]; do flag="$1" case "$flag" in -a) echo "You provided the -a flag, which takes no arguments" ;; -b) if [ $# -gt 1 ]; then arg="$2" shift else echo "You did not provide an argument for the -b flag" fi echo "You supplied an argument for the -b flag: $arg" ;; *) echo "Unrecognized flag or argument: $flag" ;; esac shift doneIf you are using bash instead of sh, you can accept both -fvalue and -f value variants:
-b*) arg="${arg##-b}" echo "You attached an argument to the -b flag: $arg" ;;My code doesn’t reset
$@. I have a README file in my homedir that reminds me of the other neat bash-ism that I like to use:${1+"$@"}expands to$@, but if$@is empty, it expands to an empty list (different from “$@” which would expand to the single empty string “”).an_agent
FAI…
shift $((OPTIND - 1))works in bash (not sh) without having to pipe to bc.
Ted Henry
Thanks for this post! This is exactly what I needed :-)
Post a Public Comment
Danny Howard is 100% responsible for the content on this site, except some of it is stolen.
All rights are reserved, unless otherwise noted. Generally, I'm a BSD guy, so you can assume implicit permission to adapt, modify, and redistribute my intellectual property with appropriate attribution. Except some of this content is itself re-appropriated, so you'd best ask first, especially for commercial use. Thanks!
You can contact me via e-mail: dannyman@toldme.com
Most of http://dannyman.toldme.com/ is powered by WordPress.
If you're hip to RSS and whatnot, you can subscribe to this site.
These links are for dannyman: login AND backlinks