tags:

views:

402

answers:

3

I have a common library that I use from several scripts that parses command line options, however I also want my individual scripts to be able to process arguments as well... e.g.

common.sh:

function get_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":ab:" optionName; do
       [ ... processing code ... ]
    done
}

a.sh

. ./common.sh

function get_local_options {
    echo -e "in getoptions"
    echo $OPTIND
    while getopts ":xy:" optionName; do
       [ ... processing code ... ]
    done
}

get_local_options $*
OPTIND=1
get_options $*

The problem si that if I call a.sh with:

a.sh -x -y foo -a -b bar

get_options stops processing at "foo" as it stops at the first "non-option"

Any way around this without rewriting things myself?

+1  A: 

I managed to get this to work, not sure if this is what you want:

$ cat common.sh
function get_options {
    while getopts ":ab:" optionName
    do
        echo "get_options: OPTARG: $OPTARG, optionName: $optionName"
    done
}
$ cat a.sh
#!/bin/bash

. ./common.sh

function get_local_options {
    while getopts ":xy:" optionName; do
        case $optionName in
            x|y) 
                echo "get_local_options: OPTARG: $OPTARG, optionName: $optionName"
                last=$OPTIND;;
            *) echo "get_local_options, done with $optionName"
                break;;
        esac;
    done
}

last=1
get_local_options $*
shift $(($last - 1))
OPTIND=1
get_options $*
$ ./a.sh -x -y foo -a -b bar
get_local_options: OPTARG: , optionName: x
get_local_options: OPTARG: foo, optionName: y
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b
$ ./a.sh -a -b bar
get_local_options, done with ?
get_options: OPTARG: , optionName: a
get_options: OPTARG: bar, optionName: b
Alok
A: 

I think you'll need to have your first getopts processor build a list of command line elements it didn't handle. Also, it will have to ignore the return value of the getopts call: getopts might return non-zero because it's hitting an argument to a command-line switch that it doesn't know how to process. So the first getopts processor needs to go to the end of the command-line. You can't trust its own judgments about when the options have stopped and the arguments have started. (Well, once you get two arguments in a row you could presumably skip the rest, but I'll leave that as an exercise.)

Something like this, then:

#!/bin/bash

UNHANDLED=()

function getxy {
  while ((OPTIND<=$#)); do
    if getopts ":xy:" opt; then
      case $opt in
        x|y) echo "getxy opt=$<$opt> OPTARG=<$OPTARG>";;
        *) UNHANDLED+=(-$OPTARG);;
      esac
    else
        UNHANDLED+=(${!OPTIND})
        let OPTIND++
    fi
  done
}

function getab {
  while getopts ":ab:" opt; do
  case $opt in
    a|b) echo "getab opt=$<$opt> OPTARG=<$OPTARG>";;
    *) echo "getab * opt=<$opt> OPTARG=<$OPTARG>";;
  esac
  done
}

echo "--- getxy ---"
OPTIND=1
getxy "$@"
# now we reset OPTIND and parse again using the UNHANDLED array
echo "--- getab ---"
OPTIND=1
set -- "${UNHANDLED[@]}"
getab "$@"
# now we get remaining args
shift $((OPTIND-1))
for arg; do
    echo "arg=<$arg>"
done
profjim
A: 
foo() {
  unset OPTIND
  while getopts ...
  do
  done
}
Bishop B