Monday, September 10, 2012

in favor of getopt() for bash arg processing « One Technical

Perhaps this is the final word, in favor of getopt() for bash arg processing:


Bash getopt versus getopts

July 16, 2012
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.

Bash getopt versus getopts « One Technical

'via Blog this'

No comments:

Post a Comment