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