|
#!/usr/bin/env bash |
|
|
|
Args(){ |
|
# Metafunction that expects "$FUNCNAME" (name of parent function), the positional parameters passed to a parent function, an ASCII "record separator" delimiter, and the variable names for the required & optional arguments |
|
# Variable names for optional arguments should be indicated by wrapping them in square brackets. A default value for optional arguments may be indicated by an equal sign, e.g. '[optionalVariable=defaultValue]' |
|
# The passed positional parameters are assigned to the variable names and returned as global variables using `printf -v` (optional positional parameters may be skipped with a quoted empty string, '') |
|
# If any required parameters are missing, printUsage() is executed (prints the expected positional parameters) and an exit code of 111 is returned |
|
local functionName posParam varName sqRegex eqRegex red reset # [https://wiki.bash-hackers.org/commands/builtin/local] |
|
functionName="$1"; shift # [https://wiki.bash-hackers.org/syntax/shellvars#funcname] [https://wiki.bash-hackers.org/commands/builtin/shift] |
|
while (( "$#" > 0 )); do [[ "$1" == $'\030' ]] && { shift; break; }; posParam+=("$1"); shift; done # [https://unix.stackexchange.com/a/628543] [https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text] |
|
while (( "$#" > 0 )); do varName+=("$1"); shift; done # [https://wiki.bash-hackers.org/scripting/posparams#loophttps://wiki.bash-hackers.org/scripting/posparams#loopss] [https://wiki.bash-hackers.org/syntax/arrays#storing_values] |
|
sqRegex='[[]([[:print:]]+)[]]' # matches one or more 'printable' character(s) (and groups each match into an array named "$BASH_REMATCH") enclosed in square brackets [https://wiki.bash-hackers.org/syntax/shellvars#bash_rematch] |
|
eqRegex='([[:alnum:]_]+)[=]([[:print:]]+)' # matches and groups one or more 'alphanumeric' or '_' character(s) followed by an '='. Matches and groups the following one or more 'printable' character(s). |
|
red="$(tput setaf 01)"; reset="$(tput sgr 0 0)" # [https://wiki.bash-hackers.org/scripting/terminalcodes#colors_using_tput] |
|
printUsage(){ printf "%s: ${red}%s\n${reset}" "usage" "${functionName} ${varName[*]}"; } # [https://wiki.bash-hackers.org/syntax/arrays#getting_values] |
|
optVar(){ |
|
if [[ "$1" =~ ${eqRegex} ]]; then # assign default value, "${BASH_REMATCH[2]}", if '=' is matched unless a value, "${posParam[i]}", is provided |
|
[[ -n "${posParam[i]}" ]] && printf -v "${BASH_REMATCH[1]}" "${posParam[i]}" || printf -v "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" |
|
elif [[ -n "${posParam[i]}" ]]; then printf -v "$1" "${posParam[i]}" |
|
fi |
|
} |
|
for (( i=0; i<${#varName[@]}; i++ )); do |
|
if [[ "${varName[i]}" =~ ${sqRegex} ]]; then optVar "${BASH_REMATCH[1]}"; |
|
elif [[ -n "${posParam[i]}" ]]; then printf -v "${varName[i]}" "${posParam[i]}"; # note that `printf -v` doesn't support direct assignment to array indexes for BASH<4.1 [https://wiki.bash-hackers.org/commands/builtin/printf#options] |
|
else printUsage && return 111 |
|
fi |
|
done |
|
} |
|
|
|
# Define an alias to pass "$FUNCNAME", positional parameters, and an ASCII "record separator" delimiter to Args() before passing the variable names. |
|
# Set a trap on ERR signal to force the parent function to break if Args() returns 111. |
|
# [https://wiki.bash-hackers.org/commands/builtin/trap] [https://stackoverflow.com/q/19091498] [https://stackoverflow.com/a/23637952/13019084] [https://stackoverflow.com/a/42954517] |
|
alias args=$'trap "(( \$? == 111 )) && return 222" ERR; Args "${FUNCNAME}" "$@" $\'\\030\'' |