while getopts "hd:R:" arg; do
case $arg in
h)
echo "usgae"
;;
d)
dir=$OPTARG
;;
R)
if [[ $OPTARG =~ ^[0-9]+$ ]];then
level=$OPTARG
else
level=1
fi
;;
\?)
echo "WRONG" >&2
;;
esac
done
level refers to parameter of
-R
, dir refers to parameters of-d
when I input
./count.sh -R 1 -d test/
it works rightlywhen I input
./count.sh -d test/ -R 1
it works rightlybut I want to make it work when I input
./count.sh -d test/ -R
or./count.sh -R -d test/
This means that I want -R
has a default value and the sequence of command could be more flexible.
The following code solves this problem by checking for a leading dash and if found decrements OPTIND to point back to the skipped option for processing. This generally works fine except that you do not know the order the user will place options on the command line - if your optional argument option is last and does not provide an argument getopts will want to error out.
To fix the problem of the final argument missing, the "$@" array simply has an empty string "$@ " appended so that getopts will be satisfied that it has gobbled up yet another option argument. To fix this new empty argument a variable is set that holds the total count of all options to be processed - when the last option is being processed a helper function called trim is called and removes the empty string prior to the value being utilized.
This is not working code, it has only place holders but you can easily modify it and with a little bit of care it can be useful to build a robust system.
Try:
I think the above code will work for your purposes while still using
getopts
. I've added the following three lines to your code whengetopts
encounters-R
:If
-R
is encountered and the first argument looks like another getopts parameter, level is set to the default value of1
, and then the$OPTIND
variable is reduced by one. The next timegetopts
goes to grab an argument, it will grab the correct argument instead of skipping it.Here is similar example based on the code from Jan Schampera's comment at this tutorial:
Wrong. Actually
getopts
does support optional arguments! From the bash man page:When the man page says "silent" it means silent error reporting. To enable it, the first character of optstring needs to be a colon:
Since Bash's getopt does not recognize
--
to end the options list, it may not work when-R
is the last option, followed by some path argument.P.S.: Traditionally, getopt.c uses two colons (
::
) to specify an optional argument. However, the version used by Bash doesn't.I agree with tripleee, getopts does not support optional argument handling.
The compromised solution I have settled on is to use the upper case/lower case combination of the same option flag to differentiate between the option that takes an argument and the other that does not.
Example:
This is actually pretty easy. Just drop the trailing colon after the R and use OPTIND
count.sh -d test
count.sh -d test -R
count.sh -R -d test
count.sh -d test -R 2
count.sh -R 2 -d test
You can always decide to differentiate the option with lowercase or uppercase.
However my idea is to call
getopts
twice and 1st time parse without arguments ignoring them (R
) then 2nd time parse only that option with argument support (R:
). The only trick is thatOPTIND
(index) needs to be changed during processing, as it keeps pointer to the current argument.Here is the code:
Here are few tests for scenarios which works:
Scenarios which doesn't work (so the code needs a bit of more tweaks):
More information about
getopts
usage can be found inman bash
.See also: Small getopts tutorial at Bash Hackers Wiki