Reputation:
I'm learning the getopt
command and using the following diagnostic script to study its workings:
$ cat test-getopt.sh
#!/bin/bash
args=`getopt ab:c $*`
set -- $args
for i
do
echo "-->$i"
done
echo $#
I cannot understand its behavour in the following cases. Could you clarify?
1st case:
$ ./test-getopt.sh -ab arg -c
-->-a
-->-b
-->arg
-->-c
-->--
5
Why does getopt
add --
as $5
? What does it mean here? To point out the end of options?
2nd case:
$ ./test-getopt.sh -ab arg c
-->-a
-- -b
-->arg
-->--
-->c
5
Now, getopt
adds c
as $5
's value, after that --
. It is not a option, what does c
mean here?
getopt
's parameter specifying valid options, why doesn't the program raise an error?I've already skimmed through the getopt
man page as well as some tutorials but couldn't quite work out a clear explanation.
Upvotes: 0
Views: 2650
Reputation: 36008
According to getopt
manpage:
Normally, no non-option parameters output is generated until all options and their arguments have been generated. Then '--' is generated as a single parameter, and after it the non-option parameters in the order they were found, each as a separate parameter.
I.e. --
by itself is generated to signify the end of options. (And after it, positional parameters are generated if there are any.)
I guess this is done for uniformity -- to use the same code logic regardless of whether the user specified --
on the command line or not.
In the 2nd case, c
is a positional argument. Positional arguments are not checked by getopt
in any way and are rather passed as-is. The manpage doesn't say anything about validating non-option arguments:
getopt is used to break up (parse) options in command lines for easy parsing by shell procedures, and to check for legal options.
Finally, note that to correctly process arguments with whitespace, you need to: use $@
instead of $*
; quoting; eval
with set
; and use the enhanced mode of getopt
-- as per Example of how to parse options with bash/getopt. Also should use bash
-e
mode to quit the program on an invalid option:
#!/bin/bash -e
args=`getopt -o ab:c -- "$@"`
eval set -- "$args"
for i
do
echo "-->$i"
done
echo $#
$ ./test-getopt.sh -b "arg ument"
-->-b
-->arg ument
-->--
3
$ ./test-getopt.sh -d ; echo $?
getopt: unknown option -- d
1
Also, a while
loop with shift
as per the same example could be more convenient that for
as it: makes it easy to get the next argument -- to get the option's argument and check if there is an argument if it's optional; check the number of the remaining (positional) arguments when you're done with options.
Upvotes: 2
Reputation: 46846
I normally use constructs like this to run getopts:
# Set defaults
opt_a=0; opt_b=""; opt_c=false
# Step through options
while getopts ab:c opt; do
case "$opt" in
a) opt_a=1 ;;
b) opt_b="${OPTARG:?The -b option requires an argument.}" ;;
c) opt_c=true ;;
*) usage; exit 64 ;;
esac
done
shift $((OPTIND - 1))
Use of shift
like this at the end causes your positional arguments to be shifted back such that the first argument that getopts
can't process becomes $1
. For example, if the above snippet was part of a script named foo
, one might run:
$ foo -ab meh smoo blarg
which would set $opt_a
to 1
, $opt_b
to "meh"
, $1
to "smoo"
and $2
to "blarg"
for the portion of the script following the snippet.
Upvotes: 0