HACKER Q&A
📣 DiggyJohnson

How do you implement option flags in your shell scripts?


I've been writing some Bash scripts for my team and I want to add option flags for a small variety of things. I'm very familiar with BASH and the shell, but have never written scripts that would be consumed by anyone other than me.

What common patterns, conventions, or idioms do you use to implement options in your shell scripts?

Examples of options I want to implement: - [-h | --help] Prints a help message. - [-t | --target] Points to a directory containing our automated tests. - [-e | --env] Takes a string URI for the automated tests to run against (e.g. http://localhost:1234 or https://tst.mycloudenv.hn). - [-q | --quiet] Suppresses output.

Capabilities: - Long or short options. - Options can appear in any order. - Options are properly interpreted anywhere in the command, besides the last position. - Short options can be concatenated.

Essentially, I want these to be as professional and POSIX compliant as possible [0].

[0] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html


  👤 LinuxBender Accepted Answer ✓
I don't have one specific method I stick with but here [1] are some popular examples.

[1] - https://stackoverflow.com/questions/7069682/how-to-get-argum...


👤 smoldesu
Red Hat has a pretty excellent article on this: https://www.redhat.com/sysadmin/arguments-options-bash-scrip...

👤 da-x
Recursive decent parsing! For example, here's a template for a script, with a `main` function, parsing for an `--interactive` flag, and handling of positional arguments. If you want an option to take an argument, you can simple extend the `case` with a new option, with shift and take `$1`.

    #!/bin/bash

    set -eu
    set -o pipefail
    set +o posix

    main() {
        local interactive=0
        local positional=()
    
        while [[ $# != 0 ]] ; do
            if [[ "$1" =~ ^--(.*)$ ]] ; then
                shift
                local param
                param="${BASH_REMATCH[1]}"
                case ${param} in
                    interactive) interactive=1 ;;
                    *) "invalid parameter ${param}"; return 1 ;;
                esac
            else
                positional+=("$1")
                shift
            fi
        done
        set -- "${positional[@]}"
    
        if [[ "${interactive}" == "1" ]] ; then
            echo "I am interactive"
        else
            echo "I am not interactive"
        fi
    
        echo "Rest of my arguments: "$@
    }
    
    main "$@"