A Simple Option Parser in Bash

There is a builtin command in bash named getopts that can process command-line options of a shell script. It works pretty well except that it can only handle single-letter options like -a, -b, -c, etc. It cannot handle long option names like --version, --verbose, and --force. For long option names, there is an external command named getopt, however for me, it is one command that is not easy to use.

Single-letter options in a command is fine until you run out of letters or you require more user-friendly usage. For example, if you need both version and verbose options, which one should you assign -v for? If you need options like --force and --file, which one should use -f for? When this happens, you’re left with no choice but to use a different character for the other option. And most of the time, you end up with a character that doesn’t make sense with the option it pertains to.

So, I decided to just implement my own parsing. It can handle both short and long option names. Below is an example.

#!/bin/bash

while true
do
  case $1 in
    --version)  # get version
        VERSION=$2
        shift; shift
        ;;
    -v) # use verbose mode
        VERBOSE=true
        shift
        ;;
    -*)
        echo "Unknown option: $1"
        exit 1
        ;;
    *)
        break
        ;;
  esac
done

echo $VERSION
echo $VERBOSE
echo $*

The bash code above handles the options --version and -v. The idea is simple. All it has to do is loop through all the command line arguments using the while and case commands. For each argument it encounters, it checks if it’s one of the options it is expecting. If the option requires a parameter, which is usually the case for long option names, it gets the value in $2 which is the next parameter since $1 contains the name of the option itself. Before proceeding to the next option, it performs a double shift command in order to set $1 as the next option name. If the option does not require a parameter, it only executes a single shift command. You can also use long option names that does not require a parameter by simply using a single shift command.

Once the script encounters a non-option argument, it breaks out of the while-loop and the rest of the arguments get assigned to $*.

For non-expected options, you search for the -* string (after exhausting all expected options) and then generate an error. If you prefer, you may just ignore it or simply pass it to the rest of the arguments as a non-option.

The code above is short and simple. It’s also easy to read for anyone who wants to know how to run your script since it’s almost self-documenting. You don’t need to write an elaborate usage documentation on what options to use.

One limitation to this parser is that all options must be specified before the non-option arguments. It’s a small price to pay for the ease of use it offers.