bashgetopts

How do I iterate over bash script flags and use them?


I have a code that I wrap with a bash script, and I want to know if a certain flag was given (-b), and if so, to update some variable I have in the script (called "x"). Unfortunately, I need this to be done on the bash script, and the synthax here drives me nuts. A small example of what I did is (without the call to the code I wrap):

#!/bin/bash
x=0

while getopts :b opt; do
  case $opt in
    b) x=1;;
    ?) echo "";;
  esac
done

echo "x is ${x}"

My problem is with having more than one flag I want to pass to the command line. I tried to use: while getopts :b:f:c opt; do..., but it looks like it fails to work when I do not give the flags in the -b -f ... -c ... order (I get x is 0 when I expect it to be x is 1).

Moreover, this does not work when I want to give flags with the -- prefix (--frame instead of -f).

What should be the code for this purpose? I tried a few options but I do not manage to figure out what should the synthax exactly.

Thank you in advance.


Solution

  • TL;DR

    Options with argument have a colon after their character in the optstring. So getopts :b:c:f means:

    If you use while getopts :b:f:c opt; do... in your script and you type:

    ./script.sh -c -b -f
    

    -b is considered as the argument of option -c, not as an option itself.

    Details

    Considering this part of your question: "it looks like it fails to work when I do not give the flags in the -b -f ... -c ... order", let's assume you want the b option to have no argument, and the f and c options to have one.

    If your option has no argument don't put a colon after it in the optstring parameter; else put the colon after the option character. With your simple example you could try:

    #!/bin/bash
    x=0
    fopt=""
    copt=""
    
    while getopts bf:c: opt; do
      case "$opt" in
        b) x=1;;
        f) echo "f option found"; fopt="$OPTARG";;
        c) echo "c option found"; copt="$OPTARG";;
        ?) echo "";;
      esac
    done
    
    echo "x is ${x}"
    echo "fopt is ${fopt}"
    echo "copt is ${copt}"
    

    And then:

    $ ./script.sh -b -c foo -f bar
    c option found
    f option found
    x is 1
    fopt is bar
    copt is foo
    $ ./script.sh -c foo -f bar
    c option found
    f option found
    x is 0
    fopt is bar
    copt is foo
    $ ./script.sh -c foo -b
    c option found
    x is 1
    fopt is 
    copt is foo
    

    But be careful: with the last example, if you omit the foo argument of the -c option:

    ./script.sh -c -b
    c option found
    x is 0
    fopt is 
    copt is -b
    

    See? -b became the missing argument and has not been considered as an option. Is it the cause of your problem?

    Note: If you want to also use long options don't use the bash getopts builtin, use the getopt utility.