Reputation: 14058
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?
Upvotes: 4
Views: 5750
Reputation: 2467
There are some good answers here but I think there is a simple solution: you need to increment OPTIND
instead of resetting it to 1. i.e.
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++ ))
get_options $*
In this way you will skip the word foo
in the args list.
Upvotes: 0
Reputation: 7572
Just have your common script provide a list of common options and functions for handling those, then have the other scripts add to the option list any options which they can handle:
common.sh
OPTS="ab:hv"
common_usage() {
echo "Common options:"
echo " -a ..."
echo " -b arg ..."
echo " -h Show usage"
echo " -v Be verbose"
}
handle_common_opts() {
case "$1" in
a) handle_opt_a ;;
b) handle_opt_b "$2" ;;
h) usage; exit 0 ;;
v) be_verbose ;;
esac
}
a.sh
. common.sh
OPTS="${OPTS}xy:"
usage() {
echo "Usage: $PROG [options] [arguments...]"
echo
common_usage
echo
echo "Specific options:"
echo " -x ..."
echo " -y arg ..."
}
while getopts "$OPTS" OPT; do
handle_common_opts "$OPT" "$OPTARG"
case "$OPT" in
x) handle_opt_x ;;
y) handle_opt_y "$OPTARG" ;;
\?) usage; exit 1 ;;
esac
done
shift $((OPTIND-1))
handle_remaining_args "$@"
This has the following advantages: no need to play tricks with the internal houskeeping of getopts
, the user can specify common and specific options in any order, it handles unrecognized options in a single place, and also makes the usage functions easier to maintain.
Upvotes: 2
Reputation: 4802
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
Upvotes: 0
Reputation: 96111
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
Upvotes: 1