Skip to content

Instantly share code, notes, and snippets.

@cosimo
Created September 21, 2012 09:31
Show Gist options
  • Save cosimo/3760587 to your computer and use it in GitHub Desktop.
Save cosimo/3760587 to your computer and use it in GitHub Desktop.
Example of how to parse options with bash/getopt
#!/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
@drveera
Copy link

drveera commented May 18, 2016

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 ?

@demonbane
Copy link

@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

@aguformoso
Copy link

A subtlety, I woud change shift; shift for shift 2.

@joonas-fi
Copy link

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 of stack-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?

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 -- "$@"`

@Decstasy
Copy link

Decstasy commented Jun 29, 2017

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

@gihankarunarathne
Copy link

@davidechicco
Copy link

davidechicco commented Jul 17, 2017

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!

@sw-carlin
Copy link

@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

@ascherer
Copy link

ascherer commented Jul 21, 2018

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.

@wilderfield
Copy link

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?

@Kocjan
Copy link

Kocjan commented Jan 8, 2019

It doesn't work me also, same problem like @wilderfield explained (MAC OS).

@aceeric
Copy link

aceeric commented Aug 16, 2019

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...

@seanw2020
Copy link

seanw2020 commented Oct 4, 2019

@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.

@severo
Copy link

severo commented May 7, 2020

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment