Monday, September 10, 2012

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'

No comments:

Post a Comment