Showing posts with label bash_getopt. Show all posts
Showing posts with label bash_getopt. Show all posts

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'

bash option processing part 2: getopt()

Just when you thought there was one safe choice to make for bash arg processing...   :-)

Bash getopts command - Stack Overflow:

I am following IBM's example from their website:
#!/bin/bash
echo "OPTIND starts at $OPTIND"
while getopts ":pq:" optname
  do
    case "$optname" in
      "p")
        echo "Option $optname is specified"
        ;;
      "q")
        echo "Option $optname has value $OPTARG"
        ;;
      "?")
        echo "Unknown option $OPTARG"
        ;;
      ":")
        echo "No argument value for option $OPTARG"
        ;;
      *)
      # Should not occur
        echo "Unknown error while processing options"
        ;;
    esac
    echo "OPTIND is now $OPTIND"
  done
All I want to to is have an option whose name is more than 1 letter. ie -pppp and -qqqq instead of -p and -q.
I have written my program and implementing -help is giving me a problem...
share|edit


Upfloor's are right. the getopt utility support long options while you can use --option. Maybe you can try this.
#!/bin/bash
args=`getopt -l help :pq: $*`
for i in $args; do
    case $i in
    -p) echo "-p"
        ;;
    -q) shift;
        optarg=$1;
        echo "-q $optarg"
        ;;
    --help)
        echo "--help"
        ;;
    esac
done
share|edit

'via Blog this'

bash option processing part 1: getopts()

Some useful general info about bash argument processing using getopts(), which is apparently the bash built-in that is easily available.  It does not support long options, and to use the info below, you will have to ignore the fact that the person below is really trying to implement mkdir -p - it still makes a useful example.

Cheers,
Connie
P.S. There is a controversy about what bash option processing tool to use, since this one (getopts) does not support long options, while "getopt" does.  Hard to pick a winner, both seem to have defects. More about the other tool in the next post.

BASH: getopts retrieving multiple variables from one flag - Stack Overflow


I am stuck and need your guys help with with getops
I created a bash script which looks like this when run:
$ foo.sh -i env -d directory -s subdirectory -f file
It works correctly when handling one argument from each flag. But when I envoke several arguements from each flag I am not sure how to pull the multiple varibale information out of the variables in getops.
while getopts ":i:d:s:f:" opt
   do
     case $opt in
        i ) initial=$OPTARG;;
        d ) dir=$OPTARG;;
        s ) sub=$OPTARG;;
        f ) files=$OPTARG;;

     esac
done
After grabbing the options I then want to build directory structures from the variables
foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
Then the directory structure would be
/test/directory/subdirectory/file1/test/directory/subdirectory/file2/test/directory/subdirectory/file3/test/directory/subdirectory2/file1/test/directory/subdirectory2/file2/test/directory/subdirectory2/file3
Any ideas?
share|edit
feedback
getopts options can only take zero or one argument. You might want to change your interface to remove the -f option, and just iterate over the remaining non-option arguments
usage: foo.sh -i end -d dir -s subdir file [...]
So,
while getopts ":i:d:s:" opt; do
  case "$opt" in
    i) initial=$OPTARG ;;
    d) dir=$OPTARG ;;
    s) sub=$OPTARG ;;
  esac
done
shift $(( OPTIND - 1 ))

path="/$initial/$dir/$sub"
mkdir -p "$path"
for file in "$@"; do
  touch "$path/$file"
done
share|edit
would getopt be a better option for this or would you use somehting different? – vegasbrianc Sep 23 '11 at 14:25
I would use just what I wrote above. If you want to be able to provide the -f option with multiple arguments, or to be able to provide -f with one argument multiple times, I know you can do that in Perl with the Getopt::Long module. – glenn jackman Sep 23 '11 at 14:59
I agree with Glenn, this is normally what I use. However, another option is to just use another delimiter e.g. commas to separate the multiple arguments instead of spaces and then split $OPTARG on comma. For example -f file1,file2,file3. I tend to only do this on commands i plan to keep to myself as I don't trust others to realize they must not put spaces after the commas – frankc Sep 23 '11 at 19:21

...
As you don't show how you hope to construct your list
/test/directory/subdirectory/file1. . .
test/directory/subdirectory2/file3
it's a little unclear how to proceed, but basically you need to keep appending any new values to the appropriate variable, i.e.
 case $opt in
    d ) dirList="${dirList} $OPTARG" ;;
 esac
Note that on the first pass dir will be empty, and you'll wind up with a space leading at the from of your final value for ${dirList}. (If you really need code that doesn't include any extra spaces, front or back, there is a command I can show you, but it will be hard to understand, and it doesn't seem that you'll need it here, but let me know)
You can then wrap your list variables in for loops to emit all the values, i.e.
for dir in ${dirList} do
   for f in ${fileList} ; do
      echo $dir/$f
   done
done
Finally, it is considered good practice to 'trap' any unknown inputs to your case statement, i.e.
 case $opt in
    i ) initial=$OPTARG;;
    d ) dir=$OPTARG;;
    s ) sub=$OPTARG;;
    f ) files=$OPTARG;;
    * ) 
       printf "unknown flag supplied "${OPTARG}\nUsageMessageGoesHere\n"
       exit 1
    ;;

 esac 
I hope this helps.
share|edit


'via Blog this'