Last active
July 19, 2025 09:18
-
-
Save guilt/064eb31172aca965e86bb19dfd18fea2 to your computer and use it in GitHub Desktop.
Argument Parsing in Bash
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env sh | |
| # Variable for the script description | |
| _ARGPARSE_SCRIPT_DESCRIPTION="" | |
| # Use this one wierd trick to shorten your CLI Boolean Flags | |
| _ARGPARSE_FLAG_BEHAVIOUR="Add this flag to set the argument to 'true' (don't use '--flag true')" | |
| # Declare an associative array for argument properties | |
| _ARGPARSE_ARG_VALUES=() | |
| # Default Enums and Values | |
| _ARGPARSE_NULL_VALUE_="null" | |
| _ARGPARSE_DEFAULT_=1 | |
| _ARGPARSE_HELP_=2 | |
| _ARGPARSE_TYPE_=3 | |
| _ARGPARSE_OPTIONAL_=4 | |
| _ARGPARSE_NUM_VALUES_=5 # Total number of properties per argument | |
| # Debug printing helpers | |
| if [ -t 1 ]; then | |
| _COLOR_ERR_SET="$(tput setaf 1)" | |
| _COLOR_SET="$(tput setaf 4)" | |
| _COLOR_RESET="$(tput sgr0)" | |
| else | |
| _COLOR_ERR_SET=${_COLOR_ERR_SET:-} | |
| _COLOR_SET=${_COLOR_SET:-} | |
| _COLOR_RESET=${_COLOR_RESET:-} | |
| fi | |
| # Function to set the script description | |
| # Usage: setDescription "Description text" | |
| setDescription() { | |
| _ARGPARSE_SCRIPT_DESCRIPTION="$1" | |
| } | |
| # Function to display help | |
| # Usage: showHelp | |
| showHelp() { | |
| local args=( "${_ARGPARSE_ARG_VALUES[@]}" ) | |
| local prefix=" " | |
| echo "\nUsage: $0 [arguments...]" | |
| echo "" | |
| echo "$_ARGPARSE_SCRIPT_DESCRIPTION" | |
| echo "" | |
| echo "Arguments:" | |
| for ((i=0; i<${#args[@]}; i+=$_ARGPARSE_NUM_VALUES_)); do | |
| [[ ${args[i+_ARGPARSE_DEFAULT_]} == "$_ARGPARSE_NULL_VALUE_" ]] && args[i+_ARGPARSE_DEFAULT_]='' | |
| if [[ ${args[i+_ARGPARSE_TYPE_]} == "bool" ]]; then | |
| args[i+_ARGPARSE_TYPE_]='' | |
| args[i+_ARGPARSE_HELP_]="${args[i+_ARGPARSE_HELP_]} $_ARGPARSE_FLAG_BEHAVIOUR" | |
| else | |
| args[i+_ARGPARSE_TYPE_]="<${args[i+_ARGPARSE_TYPE_]}>" | |
| fi | |
| printf "%s ${_COLOR_SET}%-20s${_COLOR_RESET}: (%8s) %s\n" "$prefix" "--${args[i]} ${args[i+_ARGPARSE_TYPE_]}" "${args[i+_ARGPARSE_OPTIONAL_]}" "${args[i+_ARGPARSE_HELP_]} (defaults to '${args[i+_ARGPARSE_DEFAULT_]}')" | |
| done | |
| printf "%s ${_COLOR_SET}%-20s${_COLOR_RESET}: Display this help\n" "$prefix" "-h | --help" | |
| } | |
| # Function Alias to display help | |
| # Usage: usage | |
| usage() | |
| { | |
| showHelp | |
| } | |
| # Internal Helper Function to check an Argument | |
| # Usage: _checkRequired "$@" | |
| _checkRequired() { | |
| for ((i=0; i<${#_ARGPARSE_ARG_VALUES[@]}; i+=$_ARGPARSE_NUM_VALUES_)); do | |
| if [[ "${_ARGPARSE_ARG_VALUES[i+_ARGPARSE_OPTIONAL_]}" == "required" ]]; then | |
| if ! (echo "$@" | grep "${_ARGPARSE_ARG_VALUES[i]}") >/dev/null ; then | |
| echo "${_COLOR_ERR_SET}Error${_COLOR_RESET}: '--${_ARGPARSE_ARG_VALUES[i]}' is required" >&2 | |
| showHelp >&2 | |
| exit 1 | |
| fi | |
| fi | |
| done | |
| } | |
| # Function to define a command-line argument | |
| # Usage: defineArgument "argumentName" ["default"] ["help text"] ["type"] ["required"] | |
| defineArgument() { | |
| argumentName=$1 | |
| argumentValue=${2:-"$_ARGPARSE_NULL_VALUE_"} | |
| argumentHelp=${3:-""} | |
| argumentType=${4:-"string"} | |
| argumentIsOptional=${5:-"optional"} | |
| if [[ "$argumentIsOptional" == "required" ]]; then | |
| argumentValue="$_ARGPARSE_NULL_VALUE_" | |
| fi | |
| _ARGPARSE_ARG_VALUES+=("$argumentName" "$argumentValue" "$argumentHelp" "$argumentType" "$argumentIsOptional") | |
| if [[ "$argumentValue" == "$_ARGPARSE_NULL_VALUE_" ]]; then | |
| argumentValue="" | |
| fi | |
| export "$argumentName"="$argumentValue" | |
| } | |
| # Function to parse command-line arguments | |
| # Usage: parseArgs "$@" | |
| parseArgs() { | |
| # Set Description | |
| _setDefaultDescription | |
| # Check for help | |
| _checkForHelp "$@" | |
| # Check for missing required arguments | |
| _checkRequired "$@" | |
| while [[ $# -gt 0 ]]; do | |
| key="${1#--}" # remove '--' prefix | |
| if ! (echo "${_ARGPARSE_ARG_VALUES[*]}" | tr " " "," | grep "$key," > /dev/null ); then | |
| echo "${_COLOR_ERR_SET}Error${_COLOR_RESET}: Unknown argument '$key'" >&2 | |
| showHelp >&2 | |
| exit 1 | |
| fi | |
| for ((i=0; i<${#_ARGPARSE_ARG_VALUES[*]}; i+=5)); do | |
| if [[ "$key" == "${_ARGPARSE_ARG_VALUES[i]}" ]]; then | |
| case ${_ARGPARSE_ARG_VALUES[i+_ARGPARSE_TYPE_]} in | |
| 'string') | |
| if [[ -z "${2:-}" || "${2:-}" == --* ]]; then | |
| echo "${_COLOR_ERR_SET}Error${_COLOR_RESET}: Missing value for argument --$key" >&2 | |
| exit 1 | |
| else | |
| export "$key"="$2" | |
| shift 2 | |
| fi | |
| ;; | |
| 'bool') | |
| if [[ -z "${2:-}" || "${2:-}" == --* ]]; then | |
| export "$key"='true' | |
| shift | |
| else | |
| echo "${_COLOR_ERR_SET}Error${_COLOR_RESET}: Extra value for argument --$key" >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| *) | |
| echo "${_COLOR_ERR_SET}Error${_COLOR_RESET}: Unknown argument type '${_ARGPARSE_ARG_VALUES[i+_ARGPARSE_TYPE_]}' for '$key'" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| fi | |
| done | |
| done | |
| } | |
| # Internal Function to check for help option | |
| # Usage: _checkForHelp "$@" | |
| _checkForHelp() { | |
| if (echo "$@" | grep -- "-h" > /dev/null) || (echo "$@" | grep -- "--help" > /dev/null); then | |
| showHelp | |
| exit 0 | |
| fi | |
| } | |
| # Function to set the default script description | |
| # Usage: _setDefaultDescription | |
| _setDefaultDescription() { | |
| _ARGPARSE_SCRIPT_DESCRIPTION=${_ARGPARSE_SCRIPT_DESCRIPTION:-"Description not given."} | |
| } | |
| # Internal Main Function | |
| # Usage: main "$@" | |
| main() { | |
| setDescription "This is an argument parser, adapted from https://github.com/yaacov/argparse-sh/" | |
| defineArgument "debug" "false" "Enable debug mode" "bool" "optional" | |
| parseArgs "$@" | |
| if [[ $debug == "true" ]]; then | |
| echo "Debug mode is ON." | |
| fi | |
| exit 0 | |
| } | |
| # Standalone Invocation | |
| if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then | |
| main "$@" | |
| fi |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is adopted from: /yaacov/argparse-sh. It fixes a few known bugs, when the argument is missing or when an extra value it set erroneously.