I've been learning command line argument parsing. There are long threads about this already, I don't mean to provoke one here:
Using getopts in bash shell script to get long and short command line options
How do I parse command line arguments in Bash?
Using getopts, if you want to parse an argument/value pair like "--opt value", one way is to let getopts treat this as an argument named "-" and the value becomes "-opt". Then we parse that and take the user value by the symbol ${!OPTIND}
. I need to know more about it.
In the first thread I cited above, ${!OPTIND}
was used, somebody said "what's that?" and answer was "its an indirect substitution". After reading notes on indirect references, especially https://unix.stackexchange.com/questions/41292/variable-substitution-with-an-exclamation-mark-in-bash and http://mywiki.wooledge.org/BashFAQ/006, I generally understand indirection, but I do not understand ${!OPTIND}
as an example of it.
The value of $OPTIND
is an integer, index of next command line argument. It is not a value in another array.
In the BashFAQ/006 link above, there are warnings about indirection and general advice not to use it. Maybe it is no big deal, but I'd like to avoid danger where possible.
Could we avoid indirection? Seems like I ought to be able to just use ${OPTIND}
as an integer to take a value from $@
, $@[$OPTIND]}
.
In case you want example, here is a script I called "cli-6.sh" and it will receive long form arguments WITHOUT equal signs. Run like this:
$ ./cli-6.sh -v --fred good --barney bad --wilma happy
Leave off -v for less verbosity.
$ ./cli-6.sh --fred good --barney bad --wilma happy
After Parsing values, ordinary getopts
VERBOSE 0
Arrays of opts and values
optary: fred barney wilma
valary: good bad happy
Hopefully, this runs for you too :) I did not use an associative array to hold the values because I had hope this would work in other shells, eventually.
#/usr/bin/env bash
die() {
printf '%s\n' "$1" >&2
exit 1
}
printparse(){
if [ ${VERBOSE} -gt 0 ]; then
printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
fi
}
showme(){
if [ ${VERBOSE} -gt 0 ]; then
printf 'VERBOSE: %s\n' "$1" >&2;
fi
}
VERBOSE=0
## index for imported values in optary and valary arrays
idx=0
## Need v first so VERBOSE is set early
optspec=":vh-:"
while getopts "$optspec" OPTCHAR; do
case "${OPTCHAR}" in
-)
showme "OPTARG: ${OPTARG[*]}"
showme "OPTIND: ${OPTIND[*]}"
showme "OPTCHAR: ${OPTCHAR}"
showme "There is no equal sign in ${OPTARG}"
opt=${OPTARG}
val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
printparse "--${OPTARG}" " " "\"${val}\""
if [[ "$val" == -* ]]; then
die "ERROR: $opt value must be supplied"
fi
optary[${idx}]=${opt}
valary[${idx}]=${val}
idx=$(($idx + 1))
;;
h)
echo "usage: $0 [-v] [--anyOptYouQant[=]<valueIsRequired>] [--another[=]<value>]" >&2
exit 2
;;
v)
## if -v flag is present, it means TRUE
VERBOSE=1
;;
*)
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
die "Undefined argument: '-${OPTARG}'"
fi
;;
esac
done
echo "After Parsing values, ordinary getopts"
echo "VERBOSE $VERBOSE"
echo 'Arrays of opts and values'
echo "optary: ${optary[*]}"
echo "valary: ${valary[*]}"