0

Is this the correct way to get and set with OPTS?

I want to be able to take in a lot of options like

./runthis.sh --option1 setuff --option2 setmorestuff

while :
do
        case $1 in
                -h | --help | -\?)
                        usage
                        exit 0
                        ;;
                -o | --option1)
                        O=$2
                        shift 2
                        ;;
                -z | --option2)
                        Z=$2
                        shift 2
                        ;;
                --) # End of all options
                        shift
                        break
                        ;;
                -*)
                        echo "WARN: Unknown option (ignored): $1" >&2
                        shift
                        ;;
                *)  # no more options. Stop while loop
                        break
                        ;;
        esac
done
amanada.williams
  • 467
  • 2
  • 4
  • 9

1 Answers1

1

You have two options readily available to you if you want to parse options:

  1. getopts (you can look up a getopts tutorial for this one)
  2. manual parsing (which I'll cover here)

Using getopts can be easier, but isn't as flexible. Your code is mostly correct, but isn't suited for more general option parsing.

  1. As stated by chepner, while true is the same as while :. The former is preferred since it is immediately intuitive what it is doing. This is bash we're talking about, not perl. No needed for unnecessary obfuscation.
  2. while true is not a good loop termination condition for parsing options. If you think about it, it tells you nothing of what the loop is doing. I have to go all the way to your last case to understand what's going on. The proper loop termination condition is when you've run out of command-line arguments.
  3. It isn't immediately obvious our loop is doing any form of uniform looping. I have to read each case to see that you're shifting the positional parameters array. Since you're going to be doing at least one shift at every iteration, do so.
  4. Tying in to #2, using *) to check loop termination isn't a good idea. Empty string would match that:

    ./foo.sh --option1 "" --option2 123

    As you can see, option2 would never get it's value using your code. Fortunately #2 will fix this problem too.

  5. One last point: you should probably quote all variables, unless you are sure they will not contain spaces. Whitespace splitting in bash happens AFTER variable substitution, so spaces can really screw you up. If you're not sure, quote it.

A revised version of you code could be:

while [ $# -gt 0 ]
do
        case "$1" in
                -h | --help | -\?)
                        usage
                        exit 0
                        ;;
                -o | --option1)
                        O="$2"
                        shift
                        ;;
                -z | --option2)
                        Z="$2"
                        shift
                        ;;
                --) # End of all options
                        shift
                        break
                        ;;
                *)
                        echo "WARN: Unknown option (ignored): $1" >&2
                        break
                        ;;
        esac
        shift
done
Master Chief
  • 211
  • 1
  • 4