#!/usr/bin/env bash declare -gA ARGP declare -gA SHORT __reset_argparse() { declare -gA ARGP declare -gA SHORT } die() { echo "Error: $*"; exit 1; } def_arg() { test "$3" = bool -o "$3" = value || die "def_arg: type (\$3) must be 'bool' or 'value', received '$3'" ARGP["$1,type"]="$3" if [ "$3" = bool ]; then test -z "$4" || die "def_arg: bool args cannot specify required/default" declare -g "$1=0" else test "$4" = required -o "$4" = default || die "def_arg: \$4 must be 'required' or 'default', received '$4'" if [ "$4" = required ]; then ARGP["$1,required"]=1 else value="$5" declare -g "$1=$value" fi fi if [ -n "$2" ]; then SHORT["$2"]="$1" fi } parse_args() { positional=() while (( $# > 0 )); do next="$1"; shift if [[ "$next" == --* ]]; then arg=${next#--} test -n "${ARGP["$arg,type"]}" || die "unknown arg --$arg" if [ "${ARGP["$arg,type"]}" = bool ]; then value=1 elif [ $# -eq 0 ]; then die "Missing value for --$arg" else value="$1"; shift fi declare -g "$arg=$value" elif [[ "$next" == -* ]]; then for ((i = 1 ; i < ${#next} ; i++ )); do short=${next:$i:1} arg=${SHORT["$short"]} test -n "$arg" || die "unknown short arg -$short" if [ "${ARGP["$arg,type"]}" = bool ]; then declare -g "$arg=1" else value=${next:$(( i+1 ))} if [ -z "$value" ]; then value="$1"; shift fi test -n "$value" || die "missing value for -$short" declare -g "$arg=$value" break fi done else positional+=("$next") fi done # check for missing required for arg in "${!ARGP[@]}"; do arg="${arg%%,*}" [ "${ARGP["$arg,required"]}" != 1 ] || [ -n "${!arg}" ] || die "missing argument --$arg" done }