Skip to content

Instantly share code, notes, and snippets.

@M1lan
Last active July 30, 2025 21:17
Show Gist options
  • Save M1lan/1454743f94f455a0aeff692c29a71ea0 to your computer and use it in GitHub Desktop.
Save M1lan/1454743f94f455a0aeff692c29a71ea0 to your computer and use it in GitHub Desktop.

Revisions

  1. M1lan revised this gist Jul 30, 2025. 1 changed file with 379 additions and 129 deletions.
    508 changes: 379 additions & 129 deletions template.bash
    Original file line number Diff line number Diff line change
    @@ -1,77 +1,131 @@
    #!/usr/bin/env bash

    ##
    # Note: almost always, we want to inherit the encapsulating shell
    # environment. If we don't, we can run scripts from within a clean
    # shell environment like this: #!/usr/bin/env -iS -- bash
    # Note: almost always, we /want/ to inherit the encapsulating shell
    # environment. If we don't want or "trust" that environment, we can
    # run scripts from within a clean shell environment like this:
    # #!/usr/bin/env -iS -- bash
    #
    # If anyone knows about an actual use case for this, pls let me know.

    ##
    # Shellcheck configuration.
    # Shellcheck configuration example for your .shellcheckrc:
    #
    # shellcheck enable=avoid-nullary-conditions
    # shellcheck enable=add-default-case,check-extra-masked-returns
    # shellcheck enable=check-set-e-suppressed,require-double-brackets
    # shellcheck enable=check-unassigned-uppercase,deprecate-which
    #

    #####################################################################
    # production ready bash #
    #####################################################################
    # enable=add-default-case
    # enable=avoid-nullary-conditions
    # enable=check-extra-masked-returns
    # enable=check-set-e-suppressed
    # enable=check-unassigned-uppercase
    # enable=deprecate-which
    # enable=quote-safe-variables
    # enable=require-double-brackets
    # enable=require-variable-braces
    # external-sources=true

    ##
    # This is a simple template for production ready Bash scripts.
    # In production scenarios, we want to know about all failure
    # conditions and ensure they are handled correctly. Bash is not the
    # best language for that and we need to know the subtle differences
    # between exit codes and what is printed to stdout/stderr and what
    # means sucess or failure in the specific case we execute scripted
    # commands or external commands. Using `set -euo pipefail` doesn't
    # guarantee anything. The idea is more that instead of keep going at
    # all costs, we want to fail early.
    #
    # It also includes a style-guide.
    # If in doubt, use Python or Go.
    # If multiple kilo-SLOC script, use Python or Go.
    #
    # In production, we want to know about all failure conditions and
    # ensure they are handled correctly. Bash is not the best language
    # for that and we need to know the subtle differences between exit
    # codes and what is printed to stdout/stderr and what means sucess or
    # failure in the specific case we execute scripted commands or
    # external commands. Using `set -euo pipefail` doesn't guarantee
    # anything. The idea is more that instead of keep going at all
    # costs, we want to fail early.
    # Instead of a library of scripts, write one cli with Go cobra/kong.
    #

    #####################################################################
    # style guide #
    #####################################################################

    ##
    # - Always use `shellcheck` or `shellharden` (or both)! The former
    # prints information about warnings and errors and how to fix them,
    # while the latter simply prints the already fixed script.
    # If you want it to be a "TUI" kind of interactive terminal tool,
    # consider Rust. Or at least Go.
    #
    # "Interactive" is irrelevant for production. apply YAGNI. What's
    # usually important for production is to fail early and give a good
    # error message, the return code is usually less important. We
    # normally treat scripts as if they are normal programs that just
    # suceed or fail, with Bash we have the luxury to do more but we
    # rarely want/need it because that assumes we have knowledge about the
    # system that we run on (which again, sysadmin experience is a whole
    # different story from learning a scripting language also used in
    # sysadmin applying it in a pure cloud environment. We can also just
    # "keep going" by appending || true which is usually bad style unless
    # we don't care about how a subshell exits.
    #
    # In any case, we need to not only know the scripting language, but
    # also understand what will happen if the script runs on a certain
    # system, and for that we need to know that system!
    #
    # Make sure to check version of Bash and understand the scripting
    # language capacities (aka bashisms) of that version. Make extra sure
    # to check the versions of the command line tools you use inside of
    # our scripts.
    #
    # - Column limit: 79 column (similar to PEP-8).
    # Make sure to check if you want/need POSIX sh in which case you
    # cannot use many of Bash's convenient language features.
    #
    # - Semi-reliable exit codes: If anything in the script exits with a
    # You don't need strict POSIX compliance for any modern scenarios, and
    # Linux itself is not strictly POSIX compliant.
    #
    # "sh" is actually still bash on most systems; only very rarely will
    # you still encounter a bourne shell or a korn shell.
    #
    # zsh won't be available anywhere else but on your MacBook bash will
    # always be available (both can be installed/updated).
    #
    # Bash (and Python, Go) is consent for scripting on Unix/Linux, while
    # many people prefer zsh or fish for interactive use.
    #
    # Your script can be portable by installing bash, or it can be
    # portable by being "posixly correct" which is highly irrelevant
    # unless you are working on older mainframes,- that certainly won't
    # ever happen in the cloud.
    #
    # Always use `shellcheck`.
    #
    # Column limit: 79 columns (similar to PEP-8).
    #
    # Semi-reliable exit codes: If anything in the script exits with a
    # non-zero exit code, we want the whole script to stop immediately
    # and also return with the same code. Never use `<command> || true`
    # unless we really don't care about <command>. C.f. BASHFAQ 105.
    #
    # - Use leading underscores on internal variable and function names

    # Use leading underscores on internal variable and function names
    # in order to avoid name collisions. For unintentionally global
    # variables defined without `local`, such as those defined outside of
    # a function or automatically through a `for` loop, prefix with
    # double underscores.
    #
    # - Preferably use braces when referencing variables, `"Hi, my name
    # is: ${NAME}."` instead of `"Hi, my name is $NAME."`. While quotes
    # are required for variable references, braces are only required in
    # some cases.

    # You don't have to /always/ use braces when referencing variables,
    # `"Hi, my name is: ${NAME}."` instead of `"Hi, my name is
    # $NAME."`. While quotes are required for variable references, braces
    # are only required in some cases.

    # Bad: `some_command $arg1 $arg2 $arg3`
    # Bad: `some_command ${arg1} ${arg2} ${arg3}`
    # Bad: `echo "somestring $somevar somestring$someothervar"
    # Good: `some_command "${arg1}" "${arg2}" "${arg3}"`
    # Good: `some_command "$arg1" "$arg2" "$arg3"`
    # Good: `echo "somestring${somevar} somestring${someothervar}"
    #
    # All 6 Good and Bad are valid, but the Good ones are readable.
    # Everything else synax results in sytax errors.
    #
    # - Prefer `printf` over `echo`. For more information, see:
    # http://unix.stackexchange.com/a/65819
    #
    # - Prefer `$_explicit_variable_name` over names like `$var`.
    #
    # - Underscores as function name prefix for internal functions
    #
    # - Use the `#!/usr/bin/env bash` shebang in order to run the
    # preferred Bash version rather than hard-coding the executable
    # path.
    #
    # For simple option parsing, getopts can be sufficient:
    # while getopts "hdx:o:" opt; do ... done
    # For complex cases with long options, use the manual parsing shown below.

    #####################################################################
    # header #
    @@ -86,8 +140,23 @@ set -o pipefail
    # filenames might have spaces
    IFS=$'\n\t'

    # Version information
    readonly _VERSION="1.0.0"
    # Declare and assign separately to avoid masking return values
    _BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
    readonly _BUILD_DATE

    # This script's basename.
    _SELF="$(basename "${0}")"
    readonly _SELF

    # Save original directory and script directory
    readonly _ORIGINAL_DIR="${PWD}"
    _SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    readonly _SCRIPT_DIR

    # Temporary directory for this script
    _TEMP_DIR=""

    _PRINT_HELP=0
    _USE_DEBUG=0
    @@ -100,10 +169,67 @@ _OPTION_VALUE=
    # the filename we run the script on or an action command.
    _TARGET_FILE_OR_ACTION=

    # Log levels for structured logging
    readonly _LOG_LEVELS=(ERROR WARN INFO DEBUG)
    _LOG_LEVEL="${LOG_LEVEL:-INFO}"

    #####################################################################
    # helpers #
    #####################################################################

    ##
    # _cleanup()
    #
    # Description: Cleanup function called on exit
    _cleanup() {
    local exit_code=$?
    # Remove temp files if created
    if [[ -n "${_TEMP_DIR}" ]] && [[ -d "${_TEMP_DIR}" ]]; then
    rm -rf "${_TEMP_DIR}"
    fi
    exit "${exit_code}"
    }

    # Set traps for cleanup and interruption
    trap _cleanup EXIT
    trap '_exit_1 printf "Script interrupted\\n"' INT TERM

    ##
    # _log()
    #
    # Usage:
    # _log LEVEL "message"
    #
    # Description: Structured logging with levels
    _log() {
    local level="${1}"
    shift
    local level_num
    case "${level}" in
    ERROR) level_num=0 ;;
    WARN) level_num=1 ;;
    INFO) level_num=2 ;;
    DEBUG) level_num=3 ;;
    *) return 1 ;;
    esac

    # Compare with current log level
    local current_level_num
    case "${_LOG_LEVEL}" in
    ERROR) current_level_num=0 ;;
    WARN) current_level_num=1 ;;
    INFO) current_level_num=2 ;;
    DEBUG) current_level_num=3 ;;
    *) current_level_num=2 ;; # Default to INFO if unknown
    esac

    if [[ ${level_num} -le ${current_level_num} ]]; then
    local timestamp
    timestamp="$(date -Iseconds)" || timestamp="[timestamp error]"
    printf "[%s] %s: %s\\n" "${timestamp}" "${level}" "$*" >&2
    fi
    }

    ##
    # _debug()
    #
    @@ -114,16 +240,15 @@ _TARGET_FILE_OR_ACTION=
    #
    # Example: _debug printf "[DEBUG] Variable: %s\\n" "$0"
    _debug() {
    if ((${_USE_DEBUG:-0}))
    then
    __DEBUG_COUNTER=$((__DEBUG_COUNTER+1))
    {
    printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
    printf "🐛 %s " "$__DEBUG_COUNTER >>"
    "${@}"
    printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
    } 1>&2
    fi
    if ((${_USE_DEBUG:-0})); then
    __DEBUG_COUNTER=$((__DEBUG_COUNTER + 1))
    {
    printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
    printf "🐛 %s " "${__DEBUG_COUNTER} >>"
    "${@}"
    printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
    } 1>&2
    fi
    }

    ##
    @@ -135,11 +260,15 @@ _debug() {
    # Description: Exit with status 1 after sending a message to stdout
    # and stderr.
    _exit_1() {
    {
    printf "%s " "$(tput setaf 1 || true)!$(tput sgr0 || true)"
    "${@}"
    } 1>&2
    exit 1
    {
    if [[ -t 2 ]]; then # Check if stderr is a terminal
    printf "%s " "$(tput setaf 1 || true)!$(tput sgr0 || true)"
    else
    printf "! "
    fi
    "${@}"
    } 1>&2
    exit 1
    }

    ##
    @@ -151,10 +280,46 @@ _exit_1() {
    # Description: Print warning about a non-critical error to stdout and
    # stderr, but don't exit.
    _print_warning() {
    {
    printf "%s " "$(tput setaf 1 || true)!$(tput sgr0 || true)"
    "${@}"
    } 1>&2
    {
    if [[ -t 2 ]]; then # Check if stderr is a terminal
    printf "%s " "$(tput setaf 1 || true)!$(tput sgr0 || true)"
    else
    printf "! "
    fi
    "${@}"
    } 1>&2
    }

    ##
    # _require_commands()
    #
    # Usage:
    # _require_commands command1 command2 ...
    #
    # Description: Verify required commands exist
    _require_commands() {
    local missing=()
    for cmd in "$@"; do
    if ! command -v "${cmd}" >/dev/null 2>&1; then
    missing+=("${cmd}")
    fi
    done
    if [[ ${#missing[@]} -gt 0 ]]; then
    _exit_1 printf "Missing required commands: %s\\n" "${missing[*]}"
    fi
    }

    ##
    # _create_temp_dir()
    #
    # Usage:
    # _create_temp_dir
    #
    # Description: Create a temporary directory for this script
    _create_temp_dir() {
    _TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/${_SELF}.XXXXXX")
    readonly _TEMP_DIR
    _debug printf "Created temp directory: %s\\n" "${_TEMP_DIR}"
    }

    ##
    @@ -165,7 +330,9 @@ _print_warning() {
    #
    # Description: Print the program help information.
    _print_help() {
    cat <<EOF
    cat <<EOF
    ${_SELF} version ${_VERSION} (built ${_BUILD_DATE})
    Put your helpful text here.
    Usage:
    @@ -189,81 +356,165 @@ EOF
    # Description: For the given option, return the value or exit 1 if
    # value is blank or appears to be another option.
    __get_option_value() {
    local __arg="${1:-}"
    local __val="${2:-}"

    if [[ -n "${__val:-}" ]] && [[ ! "${__val:-}" =~ ^- ]]
    then
    printf "%s\\n" "$__val"
    else
    _exit_1 printf "%s requires a valid argument.\\n" "$__arg"
    fi
    local __arg="${1:-}"
    local __val="${2:-}"

    if [[ -n "${__val:-}" ]] && [[ ! "${__val:-}" =~ ^- ]]; then
    printf "%s\\n" "${__val}"
    else
    _exit_1 printf "%s requires a valid argument.\\n" "${__arg}"
    fi
    }

    # parse options
    while ((${#}))
    do
    __arg="${1:-}"
    __val="${2:-}"

    case "$__arg" in
    -h|--help)
    _PRINT_HELP=1
    ;;
    -d|--debug)
    _USE_DEBUG=1
    ;;
    -x|--option-x)
    _OPTION_X=1
    ;;
    -o|--option-with-value)
    _OPTION_VALUE="$(__get_option_value "$__arg" "${__val:-}")"
    shift
    ;;
    -- )
    _TARGET_FILE_OR_ACTION="$(IFS=$' '; printf "%s" "${*:2}")"
    break
    ;;
    while ((${#})); do
    __arg="${1:-}"
    __val="${2:-}"

    case "${__arg}" in
    -h | --help)
    _PRINT_HELP=1
    ;;
    -d | --debug)
    _USE_DEBUG=1
    ;;
    -x | --option-x)
    _OPTION_X=1
    ;;
    -o | --option-with-value)
    _OPTION_VALUE="$(__get_option_value "${__arg}" "${__val:-}")"
    shift
    ;;
    --)
    _TARGET_FILE_OR_ACTION="$(
    IFS=$' '
    printf "%s" "${*:2}"
    )"
    break
    ;;
    -*)
    _exit_1 printf "Unexpected option: %s\\n" "$__arg"
    ;;
    _exit_1 printf "Unexpected option: %s\\n" "${__arg}"
    ;;
    *)
    _TARGET_FILE_OR_ACTION="$(IFS=$' '; printf "%s" "${*:1}")"
    break
    ;;
    esac
    shift
    _TARGET_FILE_OR_ACTION="$(
    IFS=$' '
    printf "%s" "${*:1}"
    )"
    break
    ;;
    esac
    shift
    done

    #####################################################################
    # script starts here #
    #####################################################################

    ##
    # _example_function_returns()
    #
    # Description: Examples of proper function return values
    _example_function_returns() {
    # Return success/failure
    _check_file_exists() {
    if [[ -f "${1}" ]]; then
    return 0 # success
    else
    return 1 # failure
    fi
    }

    # Return values via stdout
    _get_computed_value() {
    local result="computed value"
    printf "%s" "${result}"
    }

    # Usage examples
    # Note: When used in 'if', set -e is disabled for the function
    local file_check_result=0

    # Temporarily disable to capture exit code
    set +e
    _check_file_exists "/etc/passwd"
    file_check_result=$?
    set -e

    if [[ ${file_check_result} -eq 0 ]]; then
    _debug printf "File exists\\n"
    fi

    local value
    value=$(_get_computed_value)
    _debug printf "Got value: %s\\n" "${value}"
    }

    ##
    # _example_array_handling()
    #
    # Description: Examples of proper array usage
    _example_array_handling() {
    # Good array practices
    local -a my_array=()
    my_array+=("element1")
    my_array+=("element with spaces")
    my_array+=("third element")

    # Iterate safely
    for element in "${my_array[@]}"; do
    _debug printf "Element: %s\\n" "${element}"
    done

    # Pass arrays to functions (bash 4.3+)
    _process_array() {
    local -n arr=$1 # nameref
    for item in "${arr[@]}"; do
    _debug printf "Processing: %s\\n" "${item}"
    done
    }

    _process_array my_array
    }

    _example() {
    _debug printf "this is a debug message...\\n"

    ## handle options
    if ((_OPTION_X))
    then
    printf "Received -x. Doing nothing.\\n"
    _debug printf "the --option-x flag is set.\\n"
    else
    _debug printf "the --option-x flag is NOT set.\\n"
    fi

    if [[ -n "$_OPTION_VALUE" ]]
    then
    printf "Option value: %s\\n" "$_OPTION_VALUE"
    fi

    if [[ -n "$_TARGET_FILE_OR_ACTION" ]]
    then
    printf "Operating on file / selected action: %s\\n" "$_TARGET_FILE_OR_ACTION"
    fi

    ## do stuff here!
    printf "Bash is here: %s\\n" "$(command -v bash || true)"
    printf "Script ran successfully.\\n"
    _debug printf "this is a debug message...\\n"

    # Log examples
    _log INFO "Script starting"
    _log DEBUG "Debug information"

    # Check for required commands (example)
    # _require_commands jq curl git

    ## handle options
    if ((_OPTION_X)); then
    printf "Received -x. Doing nothing.\\n"
    _debug printf "the --option-x flag is set.\\n"
    else
    _debug printf "the --option-x flag is NOT set.\\n"
    fi

    if [[ -n "${_OPTION_VALUE}" ]]; then
    printf "Option value: %s\\n" "${_OPTION_VALUE}"
    fi

    if [[ -n "${_TARGET_FILE_OR_ACTION}" ]]; then
    printf "Operating on file / selected action: %s\\n" "${_TARGET_FILE_OR_ACTION}"
    fi

    # Demonstrate array handling
    _example_array_handling

    # Demonstrate function returns
    _example_function_returns

    # Create temp directory if needed
    # _create_temp_dir
    # echo "temp file" > "${_TEMP_DIR}/example.txt"

    ## do stuff here!
    printf "Bash is here: %s\\n" "$(command -v bash || true)"
    printf "Script ran successfully.\\n"
    }

    # _main()
    @@ -274,12 +525,11 @@ _example() {
    # Description:
    # Entry point.
    _main() {
    if ((_PRINT_HELP))
    then
    _print_help
    else
    _example "$@"
    fi
    if ((_PRINT_HELP)); then
    _print_help
    else
    _example "$@"
    fi
    }

    _main "$@"
  2. M1lan created this gist Sep 17, 2024.
    285 changes: 285 additions & 0 deletions template.bash
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,285 @@
    #!/usr/bin/env bash

    ##
    # Note: almost always, we want to inherit the encapsulating shell
    # environment. If we don't, we can run scripts from within a clean
    # shell environment like this: #!/usr/bin/env -iS -- bash

    ##
    # Shellcheck configuration.
    #
    # shellcheck enable=avoid-nullary-conditions
    # shellcheck enable=add-default-case,check-extra-masked-returns
    # shellcheck enable=check-set-e-suppressed,require-double-brackets
    # shellcheck enable=check-unassigned-uppercase,deprecate-which
    #

    #####################################################################
    # production ready bash #
    #####################################################################

    ##
    # This is a simple template for production ready Bash scripts.
    #
    # It also includes a style-guide.
    #
    # In production, we want to know about all failure conditions and
    # ensure they are handled correctly. Bash is not the best language
    # for that and we need to know the subtle differences between exit
    # codes and what is printed to stdout/stderr and what means sucess or
    # failure in the specific case we execute scripted commands or
    # external commands. Using `set -euo pipefail` doesn't guarantee
    # anything. The idea is more that instead of keep going at all
    # costs, we want to fail early.
    #

    #####################################################################
    # style guide #
    #####################################################################

    ##
    # - Always use `shellcheck` or `shellharden` (or both)! The former
    # prints information about warnings and errors and how to fix them,
    # while the latter simply prints the already fixed script.
    #
    # - Column limit: 79 column (similar to PEP-8).
    #
    # - Semi-reliable exit codes: If anything in the script exits with a
    # non-zero exit code, we want the whole script to stop immediately
    # and also return with the same code. Never use `<command> || true`
    # unless we really don't care about <command>. C.f. BASHFAQ 105.
    #
    # - Use leading underscores on internal variable and function names
    # in order to avoid name collisions. For unintentionally global
    # variables defined without `local`, such as those defined outside of
    # a function or automatically through a `for` loop, prefix with
    # double underscores.
    #
    # - Preferably use braces when referencing variables, `"Hi, my name
    # is: ${NAME}."` instead of `"Hi, my name is $NAME."`. While quotes
    # are required for variable references, braces are only required in
    # some cases.
    # Bad: `some_command $arg1 $arg2 $arg3`
    # Bad: `some_command ${arg1} ${arg2} ${arg3}`
    # Good: `some_command "${arg1}" "${arg2}" "${arg3}"`
    # Good: `some_command "$arg1" "$arg2" "$arg3"`
    #
    # - Prefer `printf` over `echo`. For more information, see:
    # http://unix.stackexchange.com/a/65819
    #
    # - Prefer `$_explicit_variable_name` over names like `$var`.
    #
    # - Use the `#!/usr/bin/env bash` shebang in order to run the
    # preferred Bash version rather than hard-coding the executable
    # path.

    #####################################################################
    # header #
    #####################################################################

    # exit immediately on err; useful for CI/CD.
    set -e
    # exit if referencing undefined var.
    set -u
    # return exit code of failed command in pipe, not last.
    set -o pipefail
    # filenames might have spaces
    IFS=$'\n\t'

    # This script's basename.
    _SELF="$(basename "${0}")"

    _PRINT_HELP=0
    _USE_DEBUG=0
    __DEBUG_COUNTER=0

    _OPTION_X=0
    _OPTION_VALUE=

    # everything that comes after '--' is not an option flag, but usually
    # the filename we run the script on or an action command.
    _TARGET_FILE_OR_ACTION=

    #####################################################################
    # helpers #
    #####################################################################

    ##
    # _debug()
    #
    # Usage:
    # _debug <command> <options>
    #
    # Description: Print debug info to stderr.
    #
    # Example: _debug printf "[DEBUG] Variable: %s\\n" "$0"
    _debug() {
    if ((${_USE_DEBUG:-0}))
    then
    __DEBUG_COUNTER=$((__DEBUG_COUNTER+1))
    {
    printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
    printf "🐛 %s " "$__DEBUG_COUNTER >>"
    "${@}"
    printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
    } 1>&2
    fi
    }

    ##
    # _exit_1()
    #
    # Usage:
    # _exit_1 <command>
    #
    # Description: Exit with status 1 after sending a message to stdout
    # and stderr.
    _exit_1() {
    {
    printf "%s " "$(tput setaf 1 || true)!$(tput sgr0 || true)"
    "${@}"
    } 1>&2
    exit 1
    }

    ##
    # _warn()
    #
    # Usage:
    # _warn <command>
    #
    # Description: Print warning about a non-critical error to stdout and
    # stderr, but don't exit.
    _print_warning() {
    {
    printf "%s " "$(tput setaf 1 || true)!$(tput sgr0 || true)"
    "${@}"
    } 1>&2
    }

    ##
    # _print_help()
    #
    # Usage:
    # _print_help
    #
    # Description: Print the program help information.
    _print_help() {
    cat <<EOF
    Put your helpful text here.
    Usage:
    ${_SELF} [--options] [<arguments>] [--] [target, action, or file]
    ${_SELF} -h | --help
    Options:
    -h, --help Display this help information.
    -d, --debug Debug mode.
    -x, --option-x A simple option 'x'.
    -o, --option-with-value Some option, followed by a required value.
    EOF
    }

    ##
    # __get_option_value()
    #
    # Usage:
    # __get_option_value <option> <value>
    #
    # Description: For the given option, return the value or exit 1 if
    # value is blank or appears to be another option.
    __get_option_value() {
    local __arg="${1:-}"
    local __val="${2:-}"

    if [[ -n "${__val:-}" ]] && [[ ! "${__val:-}" =~ ^- ]]
    then
    printf "%s\\n" "$__val"
    else
    _exit_1 printf "%s requires a valid argument.\\n" "$__arg"
    fi
    }

    # parse options
    while ((${#}))
    do
    __arg="${1:-}"
    __val="${2:-}"

    case "$__arg" in
    -h|--help)
    _PRINT_HELP=1
    ;;
    -d|--debug)
    _USE_DEBUG=1
    ;;
    -x|--option-x)
    _OPTION_X=1
    ;;
    -o|--option-with-value)
    _OPTION_VALUE="$(__get_option_value "$__arg" "${__val:-}")"
    shift
    ;;
    -- )
    _TARGET_FILE_OR_ACTION="$(IFS=$' '; printf "%s" "${*:2}")"
    break
    ;;
    -*)
    _exit_1 printf "Unexpected option: %s\\n" "$__arg"
    ;;
    *)
    _TARGET_FILE_OR_ACTION="$(IFS=$' '; printf "%s" "${*:1}")"
    break
    ;;
    esac
    shift
    done

    #####################################################################
    # script starts here #
    #####################################################################

    _example() {
    _debug printf "this is a debug message...\\n"

    ## handle options
    if ((_OPTION_X))
    then
    printf "Received -x. Doing nothing.\\n"
    _debug printf "the --option-x flag is set.\\n"
    else
    _debug printf "the --option-x flag is NOT set.\\n"
    fi

    if [[ -n "$_OPTION_VALUE" ]]
    then
    printf "Option value: %s\\n" "$_OPTION_VALUE"
    fi

    if [[ -n "$_TARGET_FILE_OR_ACTION" ]]
    then
    printf "Operating on file / selected action: %s\\n" "$_TARGET_FILE_OR_ACTION"
    fi

    ## do stuff here!
    printf "Bash is here: %s\\n" "$(command -v bash || true)"
    printf "Script ran successfully.\\n"
    }

    # _main()
    #
    # Usage:
    # _main [<options>] [<arguments>]
    #
    # Description:
    # Entry point.
    _main() {
    if ((_PRINT_HELP))
    then
    _print_help
    else
    _example "$@"
    fi
    }

    _main "$@"