-
-
Save cosimo/3760587 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
# | |
# Example of how to parse short/long options with 'getopt' | |
# | |
OPTS=`getopt -o vhns: --long verbose,dry-run,help,stack-size: -n 'parse-options' -- "$@"` | |
if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi | |
echo "$OPTS" | |
eval set -- "$OPTS" | |
VERBOSE=false | |
HELP=false | |
DRY_RUN=false | |
STACK_SIZE=0 | |
while true; do | |
case "$1" in | |
-v | --verbose ) VERBOSE=true; shift ;; | |
-h | --help ) HELP=true; shift ;; | |
-n | --dry-run ) DRY_RUN=true; shift ;; | |
-s | --stack-size ) STACK_SIZE="$2"; shift; shift ;; | |
-- ) shift; break ;; | |
* ) break ;; | |
esac | |
done | |
echo VERBOSE=$VERBOSE | |
echo HELP=$HELP | |
echo DRY_RUN=$DRY_RUN | |
echo STACK_SIZE=$STACK_SIZE |
Super!
Not bad, not bad at all.
Do you know how to prevent passing parameters to arguments that doesn't take one ?
For example
if i call
sh parse-options.sh -h hello
it should throw an error saying that -h doesn't take parameter values
Do anyone knows how to do it ?
@veera-dr It depends on whether your program takes non-option arguments. If parse-options.sh foo bar
would be a valid invocation, you would need to follow every single option in your getopt string with ::
and then test every output option to see if it has an argument attached or not.
If your program wouldn't normally take any arguments, you could just test for arguments in *
and throw an error if you find any. For example:
while true; do
case "$1" in
-a ) OPT_A=true;;
-b ) OPT_B=true;;
-- ) ;;
* ) if [ -z "$1" ]; then break; else echo "$1 is not a valid option"; exit 1; fi;;
esac
shift
done
A subtlety, I woud change shift; shift
for shift 2
.
As stack-size
was the only option that takes a value, it would've been a better idea to not put it last:
- Why's the colon there? does the
--long
list always end with a colon, or is it part ofstack-size
option definition? - It takes a numeric value and
getopt
's-n
switch confused the hell out of me:- does it belong to the
stack-size:
option definition ("takes numeric value?") OR - is it something else?
- does it belong to the
Upon investigating, it seems to be just a switch to ´getopt´, so IMO this would have been clearer:
getopt -n 'parse-options' -o vhns: --long verbose,stack-size:,dry-run,help -- "$@"`
I have written a faster version by myself which does not need the external program getopt to parse long options. It is compatible to bash 3,4 and ksh (fastest). Surely there are more compatible shell's but this are the shells I have tested. It is a little bit more advanced than your version but easy to maintain. Click here to see it on gist.github.com
Thanks for the code. A question, for anyone who can help me: how to edit the code to let the script read another integer value as well?
For example, suppose you want to include the numeric integer -x
parameter (long: --xxx-size
).
The new input line should work for something like: ./parse-options.sh -s 20 -x 100
How can you do that? I tried by duplicating the STACK_SIZE line but it did not work. Thanks!
@davidechicco it took me a while to figure this out because get-opt is cryptic, but the colon at the end of stack-size: in the get-opt command means "requires argument". http://www.bahmanm.com/blogs/command-line-options-how-to-parse-in-bash-using-getopt
The script works just as well with #!/bin/sh
aka dash on (K)Ubuntu.
However, due to an incompatible getopt
, this script fails on BSD and MacOS.
Hey,
This actually doesn't work for me. While the variable OPTS does get appended with -- , that string does not exist in the actual command line arguments. The while loop never encounters a -- . Am I missing something?
It doesn't work me also, same problem like @wilderfield explained (MAC OS).
Question for anyone who knows: I see the asterisk case above and elsewhere. I've not been able to trigger it. Under what circumstances would the script enter that case statement? Thanks...
@aceeric, if you handle each case correctly, that should never happen. For example, if you omit the -s | --stack-size ) STACK_SIZE="$2"; shift; shift ;;
line, and then issue parse-options -s
, you'll trigger the *)
pattern's command.
@wilderfield and @Kocjan, MacOS is more FreeBSD (i.e., Unix) than Linux. If you issue man getopt
and man getopts
(note the plural spelling), you'll see results for both, but the first line of the output will say BSD
and will not mention long options. Neither BSD version allows long options like --help
or --foobar
. For that, you need to install brew install coreutils
. For details, see https://stackoverflow.com/a/47542834/1231693. After that, man getopt
will NOT say BSD
and will mention long
options.
For anyone interested in "test driving" what's really happening in this gist, try the following one-liner:
getopt -o vhns: --long verbose,dry-run,help,stack-size: -n 'parse-options' -- "$@" <<< bash foo --verbose -s 1 bar
If you vary the number and variety of options at the end, you can see how getopt works.
@davidechicco it took me a while to figure this out because get-opt is cryptic, but the colon at the end of stack-size: in the get-opt command means "requires argument". http://www.bahmanm.com/blogs/command-line-options-how-to-parse-in-bash-using-getopt
Thanks for this!!! I hadn't notice and couldn't make my script work.
Nice. Though a little explanation on what is happening inside the case/esac would help the new-bees.