Skip to content

Instantly share code, notes, and snippets.

@mcnesium
Forked from bobpaul/gnu_getopt_example.sh
Last active July 4, 2025 04:27
Show Gist options
  • Select an option

  • Save mcnesium/bbfe60e4f43554cbc2880f2e7085956d to your computer and use it in GitHub Desktop.

Select an option

Save mcnesium/bbfe60e4f43554cbc2880f2e7085956d to your computer and use it in GitHub Desktop.

Revisions

  1. mcnesium revised this gist Nov 1, 2024. 1 changed file with 30 additions and 24 deletions.
    54 changes: 30 additions & 24 deletions gnu_getopt_example.sh
    100644 → 100755
    Original file line number Diff line number Diff line change
    @@ -6,11 +6,11 @@
    # Example of using getopt to parse command line options
    # http://stackoverflow.com/a/29754866/1219634 Limitation: All the options
    # starting with - have to be listed in --options/--longoptions, else getopt will
    # error out.
    # error out.
    # The downside is that if you intend to use this as a wrapper to some other program,
    # arguments you want to pass on to that program must come after a -- argument.
    # ex: mygrep --myoption mypositional -- -r 'options_for_real_grep' *
    getopt --test 2> /dev/null
    getopt --test 2>/dev/null
    if [[ $? -ne 4 ]]; then
    echo "GNU's enhanced getopt is required to run this script"
    echo "You can usually find this in the util-linux package"
    @@ -30,7 +30,7 @@ declare -a EXTRA_ARGS
    declare -a POSITIONAL
    DISCARD_OPTS_AFTER_DOUBLEDASH=0 # 1=Discard, 0=Save opts after -- to ${EXTRA_ARGS}

    echo "All pre-getopt arguments: $@"
    echo "All pre-getopt arguments: $*"

    # An option followed by a single colon ':' means that it *needs* an argument.
    # An option followed by double colons '::' means that its argument is optional.
    @@ -43,11 +43,13 @@ LONG=help,debug,verbose,outfile: # List all the long options
    # - Pass arguments only via -- "$@" to separate them correctly.
    # - getopt auto-adds "--" at the end of ${PARSED}, which is then later set to
    # "$@" using the set command.
    PARSED=$(getopt --options ${SHORT} \
    --longoptions ${LONG} \
    --name "$0" \
    -- "$@") #Pass all the args to this script to getopt
    if [[ $? -ne 0 ]]; then
    if ! PARSED=$(
    getopt --options ${SHORT} \
    --longoptions ${LONG} \
    --name "$0" \
    -- "$@" #Pass all the args to this script to getopt
    ); then
    # if [[ $? -ne 0 ]]; then
    # e.g. $? == 1
    # then getopt has complained about wrong arguments to stdout
    exit 2
    @@ -57,27 +59,31 @@ fi
    eval set -- "${PARSED}"

    echo "Getopt parsed arguments: ${PARSED}"
    echo "Effective arguments: $@"
    echo "Effective arguments: $*"
    echo "Num args: $#"

    dashes=0 #flag to track if we've parsed '--'
    while [[ $# -gt 0 ]]; do
    echo "parsing arg: $1"
    case "$1" in
    -o|--outfile) shift
    OUT_FILE="$1";;
    -h|--help) HELP=1;;
    -D|--debug) DEBUG=1;;
    -v|--verbose) VERBOSE=1;;
    --) dashes=1
    if [[ ${DISCARD_OPTS_AFTER_DOUBLEDASH} -eq 1 ]]; then break; fi
    ;;
    *) #store positional arguments until we reach the dashes, then store as extra
    if [[ $dashes -eq 0 ]]; then POSITIONAL+=("$1");
    else EXTRA_ARGS+=("$1"); fi
    ;;
    -o | --outfile)
    shift
    OUT_FILE="$1"
    ;;
    -h | --help) HELP=1 ;;
    -D | --debug) DEBUG=1 ;;
    -v | --verbose) VERBOSE=1 ;;
    --)
    dashes=1
    if [[ ${DISCARD_OPTS_AFTER_DOUBLEDASH} -eq 1 ]]; then break; fi
    ;;
    *) #store positional arguments until we reach the dashes, then store as extra
    if [[ $dashes -eq 0 ]]; then
    POSITIONAL+=("$1")
    else EXTRA_ARGS+=("$1"); fi
    ;;
    esac
    shift # Expose the next argument
    shift # Expose the next argument
    done
    set -- "${POSITIONAL[@]}"

    @@ -95,7 +101,7 @@ while [[ $# -gt 0 ]]; do
    done

    echo "Extra arguments: (those after --)"
    for i in ${!EXTRA_ARGS[@]}; do
    for i in "${!EXTRA_ARGS[@]}"; do
    #here's the extra args you can pass to a wrapped utility
    echo "${EXTRA_ARGS[$i]}"
    done
    done
  2. @bobpaul bobpaul created this gist Mar 20, 2018.
    101 changes: 101 additions & 0 deletions gnu_getopt_example.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    #!/usr/bin/env bash
    # Time-stamp: <2017-04-27 09:57:21 kmodi>
    # Time-stamp: <2018-03-20 12:58:02 bobpaul>
    # derived from kmodi's gist: https://gist.github.com/kaushalmodi/74e9875d5ab0a2bc1010447f1bee5d0a
    #
    # Example of using getopt to parse command line options
    # http://stackoverflow.com/a/29754866/1219634 Limitation: All the options
    # starting with - have to be listed in --options/--longoptions, else getopt will
    # error out.
    # The downside is that if you intend to use this as a wrapper to some other program,
    # arguments you want to pass on to that program must come after a -- argument.
    # ex: mygrep --myoption mypositional -- -r 'options_for_real_grep' *
    getopt --test 2> /dev/null
    if [[ $? -ne 4 ]]; then
    echo "GNU's enhanced getopt is required to run this script"
    echo "You can usually find this in the util-linux package"
    echo "On MacOS/OS X see homebrew's package: http://brewformulas.org/Gnu-getopt"
    echo "For anyone else, build from source: http://frodo.looijaard.name/project/getopt"
    exit 1
    fi
    #bash strict mode: see http://redsymbol.net/articles/unofficial-bash-strict-mode/ for info
    set -euo pipefail
    IFS=$'\n\t'

    # Initialize variables
    HELP=0
    DEBUG=0
    VERBOSE=0
    declare -a EXTRA_ARGS
    declare -a POSITIONAL
    DISCARD_OPTS_AFTER_DOUBLEDASH=0 # 1=Discard, 0=Save opts after -- to ${EXTRA_ARGS}

    echo "All pre-getopt arguments: $@"

    # An option followed by a single colon ':' means that it *needs* an argument.
    # An option followed by double colons '::' means that its argument is optional.
    # See `man getopt'.
    SHORT=-hDvo: # List all the short options
    LONG=help,debug,verbose,outfile: # List all the long options

    # - Temporarily store output to be able to check for errors.
    # - Activate advanced mode getopt quoting e.g. via "--options".
    # - Pass arguments only via -- "$@" to separate them correctly.
    # - getopt auto-adds "--" at the end of ${PARSED}, which is then later set to
    # "$@" using the set command.
    PARSED=$(getopt --options ${SHORT} \
    --longoptions ${LONG} \
    --name "$0" \
    -- "$@") #Pass all the args to this script to getopt
    if [[ $? -ne 0 ]]; then
    # e.g. $? == 1
    # then getopt has complained about wrong arguments to stdout
    exit 2
    fi
    # Use eval with "$PARSED" to properly handle the quoting
    # The set command sets the list of arguments equal to ${PARSED}.
    eval set -- "${PARSED}"

    echo "Getopt parsed arguments: ${PARSED}"
    echo "Effective arguments: $@"
    echo "Num args: $#"

    dashes=0 #flag to track if we've parsed '--'
    while [[ $# -gt 0 ]]; do
    echo "parsing arg: $1"
    case "$1" in
    -o|--outfile) shift
    OUT_FILE="$1";;
    -h|--help) HELP=1;;
    -D|--debug) DEBUG=1;;
    -v|--verbose) VERBOSE=1;;
    --) dashes=1
    if [[ ${DISCARD_OPTS_AFTER_DOUBLEDASH} -eq 1 ]]; then break; fi
    ;;
    *) #store positional arguments until we reach the dashes, then store as extra
    if [[ $dashes -eq 0 ]]; then POSITIONAL+=("$1");
    else EXTRA_ARGS+=("$1"); fi
    ;;
    esac
    shift # Expose the next argument
    done
    set -- "${POSITIONAL[@]}"

    echo ""
    echo ""
    echo "Parsed options:"
    echo "help: ${HELP}, debug: ${DEBUG}, verbose: ${VERBOSE}, outfile: ${OUT_FILE:-}"

    #restore positional arguments as $1, $2, etc
    echo "Listing positional arguments:"
    while [[ $# -gt 0 ]]; do
    #here's where you can parse your positional arguments
    echo "$1"
    shift
    done

    echo "Extra arguments: (those after --)"
    for i in ${!EXTRA_ARGS[@]}; do
    #here's the extra args you can pass to a wrapped utility
    echo "${EXTRA_ARGS[$i]}"
    done