Bash getopt versus getopts
I recently reviewed someone’s bash code, and noted their use of getopt. I had always been using getopts, so was at first confused (due to syntax), then puzzled: which one is better, getopt or getopts?
Getopt is older, but is a separate binary. It tends to be pretty robust, and supports long options (i.e., you can use –foo instead of just single letter options like -f). Getopt will also re-arrange the parameters.
Getopts is newer but built into the shell. Its syntax tends to be simpler to use.
Let’s see some quick examples of usage:
#!/bin/bash # getopt.sh example # Execute getopt ARGS=$(getopt -o a:b:c -l "ay:,bee:cee" -n "getopt.sh" -- "$@"); #Bad arguments if [ $? -ne 0 ]; then exit 1 fi eval set -- "$ARGS"; while true; do case "$1" in -a|--ay) shift; if [ -n "$1" ]; then echo "-a used: $1"; shift; fi ;; -b|--bee) shift; if [ -n "$1" ]; then echo "-b used: $1"; shift; fi ;; -c|--cee) shift; echo "-c used"; ;; --) shift; break; ;; esac done
And now getopts:
#!/bin/bash # getopts example while getopts a:b:c flag; do case $flag in a) echo "-a used: $OPTARG"; ;; b) echo "-b used: $OPTARG"; ;; c) echo "-c used"; ;; ?) exit; ;; esac done shift $(( OPTIND - 1 ));
Getopts does seem much simpler. Let’s run both, and see what happens:
$ ./getopt.sh -a "opt a" -b opt_b arg1 -a used: opt a -b used: opt_b -c used $ ./getopts.sh -a "opt a" -b opt_b arg1 -a used: opt a -b used: opt_b -c used $
As expected, the output is the same. However, let’s make one small change: let’s put the argument first.
$ ./getopt.sh arg1 -a "opt a" -b opt_b -c -a used: opt a -b used: opt_b -c used $ ./getopts.sh arg1 -a "opt a" -b opt_b -c $
Whoops! Remember how getopt.sh re-arranged parameters while getopts didn’t? You need to put the arguments last, or getopts gets confused. getopt will actually re-arrange the parameters to put the options first, then add a ‘–’, then put the arguments. Let’s hack our getopt.sh script to see what happens.
#!/bin/bash # getopt.sh - modified to show $@ contents echo "BEFORE GETOPT: $@"; # Execute getopt ARGS=$(getopt -o a:b:c -l "ay:,bee:cee" -n "getopt.sh" -- "$@"); echo "AFTER GETOPT: $@"; #Bad arguments if [ $? -ne 0 ]; then exit 1 fi eval set -- "$ARGS"; echo "AFTER SET -- \$ARGS: $@"; while true; do case "$1" in -a|--ay) shift; if [ -n "$1" ]; then echo "-a used: $1"; shift; fi ;; -b|--bee) shift; if [ -n "$1" ]; then echo "-b used: $1"; shift; fi ;; -c|--cee) shift; echo "-c used"; ;; --) shift; break; ;; esac done echo "AFTER OPTION PROCESSING: $@";
And the output:
$ ./getopt.sh arg1 -a "opt a" -b opt_b -c BEFORE GETOPT: arg1 -a opt a -b opt_b -c AFTER GETOPT: arg1 -a opt a -b opt_b -c AFTER SET -- $ARGS: -a opt a -b opt_b -c -- arg1 -a used: opt a -b used: opt_b -c used AFTER OPTION PROCESSING: arg1
Neither method is wrong (and I’m sure there are more tweaks to each style I could do), but I think I’m going to lean towards the getopt camp. It’s a little more work, but seems a little more robust.
ADVERTISEMENT
Bash getopt versus getopts « One Technical
'via Blog this'
No comments:
Post a Comment