Skip to content

Latest commit

 

History

History

parse-options-via-while-and-case-not-getopts-or-getopt

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

Parse options via while and case not getopts or getopt

Unix shell scripts have a variety of ways to do options parsing such as:

There are pros and cons to each.

If you want simple parsing, then we recommend writing your own via while and case.

If you want sophisticated parsing, then we recommend changing from a Unix shell script to a more-capable programming language.

while and case

Use a while infinite loop that contains a case statement for all the command line options.

Example:

# Set verbose level to default 0 
verbose=0

# Set any options to defaults
foo=""

# Process all the command line options
while :; do
    case $1 in
        # Two hyphens ends the options parsing
        --)
            shift
            break
            ;;
        # Print help by calling a help() function that you define later
        -h|--help)
            help
            exit
            ;;
        # Each verbose option is treated as incrementing the verbose level
        -v|--verbose)
            verbose=$((verbose + 1))
            ;;
        # Parse an option value via this syntax: --foo bar
        -f|--foo)
            if [ -n "$2" ]; then
                foo=$2
                shift
            else
                die "The command option --foo requires a value"
            fi
            ;;
        # Parse an option value via this syntax: --foo=bar
        --foo=?*)
            foo=${1#*=}
            ;;
        # Parse an option value via this syntax: --foo= (i.e. blank)
        --foo=)
            die "The command option --foo requires a value"
            ;;            
        # Anything remaining that starts with a dash triggers a fatal error
        -?*)
            die "The command line option is unknown: " "$1"
            ;;
        # Anything remaining is treated as content not a parseable option
        *)
            break
            ;;
    esac
    shift
done

help(){
cat << EOF
    # add your help documentation here
EOF
}

die(){
    >&2 printf %s\\n "$*"
    exit 1
}

getops

The POSIX getopts approach can be great for combining one-letter options, such as the classic ps -aux.

Unfortunately POSIX getopts doesn't support long options, such as --help. This means a script can't use long options that are descriptive such as --dry-run, --danger, --minimum, --maximum, etc.

For some teams, and some commands, the long options are worth the tradeoff because they improve readability, as well as flexibility when a programmer is creating a script's option names. For a majority of our use cases, we value the long options more highly than the short options combining letters, and we're willing to write the parsing ourselves.

getops versus getopt

In practice there are significant differences between getopts and getopt and their various versions on various systems:

  • The getopts POSIX standard can parse a short option, but not a long option, whereas getoptcan do both. Example: "-h" is a short option, and "--help" is a long option.

  • The getopt tool has had parsing issues when there is an empty argument string. Example: "--foo=bar" succeeds, whereas "--foo=" fails even though it could mean that foo should be set to a blank string.

  • Different capabilities can happen depending on the specific system. Example: some systems do not come with getopt, whereas some systems do come with getopt yet there are different versions with different capabilties due to use of different package sources such as util-linux versus linux-utils.

getopts_long

This is written as a POSIX shell function that we embed within a shell script.

When you want the best capabilties for options parsing, and you do not want to change from a Unix shell script to a more-capable programming language, then try getopts_long.

zparseopts

This is an approach that is specific to Z shell (zsh).

When you have a specification that states the shell script will exclusively run on Z shell, then try zparseopts.