diff --git a/examples/parsing_function_args.sh b/examples/parsing_function_args.sh new file mode 100755 index 0000000..c62f65f --- /dev/null +++ b/examples/parsing_function_args.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +# demonstrate use of getoptions for parsing function arguments. +# getoptions was designed for parsing command arguments but works great for functions as well. +# when a function is called, check to see if the parser function exists. +# if it does not exist then create it using getoptions and a parser definition. +# the parser definition is specified internally to the function but it will have global shell scope. +# + +set -euo pipefail + +source ./getoptions.sh + +function myfunc() { + # declare all function parameters and the helper variable as local variable + local my_flag + local my_param + local my_option + # shellcheck disable=SC2034 + local my_version + local my_param2="world" + local my_option2 + + # declare the helper variable args specified in the parser definition as local + local args + + # param2 has a prior value that is not specified in the function call + # shellcheck disable=SC2034 + local param2_current="hello" + # shellcheck disable=SC2034 + local my_option2_current="quick" + + local parser_name="${FUNCNAME[0]}_parser" + if [ ! "$(type -t "${parser_name}")" == 'function' ]; then + + myfunc_parser_def() { + setup args plus:true help:usage abbr:true -- "Usage: myfunc [options...] [arguments...]" '' + msg -- 'Options:' + # shellcheck disable=SC1083 + flag my_flag -f +f --{no-}flag + flag my_verbose -v --verbose counter:true init:=0 + param my_param -p --param init:="foo" pattern:"foo | bar" + param my_param2 -q --param2 init:="${my_param2}" + option my_option -o --option validate:number + # shellcheck disable=SC1083 + option my_option2 -n +n --{no-}option2 init:="missing value" on:"on value" no:"no value" + disp :usage -h --help + disp my_version --version + } + + number() { + case $OPTARG in (*[!0-9]*) + return 1 + esac + } + echo "creating parser ${parser_name} from ${parser_name}_def" + eval "$(getoptions "${parser_name}_def" "${parser_name}")" + fi + + # call the function arg parser + "${parser_name}" "${@}" + # reset the stack $@ variable to the positional arguments + eval "set -- ${args}" + + # shellcheck disable=SC2034 + readonly my_flag my_param my_option my_version my_param2 my_option2 + + #printf "my_flag: %s, my_param: %s, my_option: %s, my_param2: %s, my_option2: %s\n" "${my_flag}" "${my_param}" "${my_option}" "${my_param2}" "${my_option2}" + + echo " -- arguments begin" + local arg + for arg in "${@}"; do + printf " %s=%s\n" "${arg}" "${!arg}" + done + echo " -- arguments end" +} + + +echo "-- parameter with default value foo when parameter is not specified" +myfunc my_param + +echo "-- parameter with explicit value bar" +myfunc -p bar my_param + +echo "-- long form parameter with explicit value bar" +myfunc --param bar my_param + +echo "-- option2 when not specified results in default 'missing value'" +myfunc my_option2 + +echo "-- option2 explicit on without value results in 'on value'" +myfunc -n my_option2 +myfunc --option2 my_option2 + +echo "-- option2 explicit off without value results in 'no value'" +myfunc +n my_option2 +myfunc --no-option2 my_option2 + +echo "-- option2 with explicit value results in 'myvalue'" +myfunc -nmyvalue my_option2 +myfunc --option2=myvalue my_option2 + +#echo "-- general test" +myfunc -f --flag -p foo --param bar -o1 --option=2 -n -q "some_value" -- my_param my_flag my_param my_param2 my_option my_option2 +#myfunc -f --flag -p foo --param bar -o1 --option=9 --option2="some_option" -q "some_param" "A" "B" "C" +#myfunc -f --flag -p foo --param bar -o1 --option=9 -q "some_param" "A" "B" "C" diff --git a/lib/getoptions_abbr.sh b/lib/getoptions_abbr.sh index 3972fb5..d496e92 100644 --- a/lib/getoptions_abbr.sh +++ b/lib/getoptions_abbr.sh @@ -44,9 +44,9 @@ getoptions_abbr() { _4 'OPTIND=$((($#+1)/2)) OPTARG=$1; shift' _4 'while [ $# -gt "$OPTIND" ]; do OPTARG="$OPTARG, $1"; shift; done' _4 'set "Ambiguous option: $1 (could be $OPTARG)" ambiguous "$@"' - [ "$_error" ] && _4 "$_error" '"$@" >&2 || exit $?' + [ "$_error" ] && _4 "$_error" '"$@" >&2 || return $?' _4 'echo "$1" >&2' - _4 'exit 1 ;;' + _4 'return 1 ;;' _3 '?*)' _4 '[ "$2" = "$3" ] || OPTARG="$OPTARG=$2"' _4 "shift 3; eval 'set -- \"\${OPTARG# }\"' \${1+'\"\$@\"'}; OPTARG= ;;" diff --git a/lib/getoptions_base.sh b/lib/getoptions_base.sh index 9cd4810..a308cca 100644 --- a/lib/getoptions_base.sh +++ b/lib/getoptions_base.sh @@ -56,6 +56,7 @@ getoptions() { [ "${1#-}" ] && _rest=$1 while loop "$@" && shift; do kv "$1" _; done } + _0 "${_def:-$2}() {" _flag() { args '' "$@"; defvar "$@"; } _param() { args 1 "$@"; defvar "$@"; } _option() { args 1 "$@"; defvar "$@"; } @@ -67,7 +68,6 @@ getoptions() { cmd() { :; } _0 "${_rest:?}=''" - _0 "${_def:-$2}() {" _1 'OPTIND=$(($#+1))' _1 'while OPTARG= && [ $# -gt 0 ]; do' [ "$_abbr" ] && getoptions_abbr "$@" @@ -128,7 +128,7 @@ getoptions() { args "$@" _3 "$sw)" code "$1" _4 "echo \"\${$1}\"" "${1#:}" - _4 'exit 0 ;;' + _4 'return 0 ;;' } _msg() { :; } @@ -188,12 +188,12 @@ getoptions() { _2 'notcmd) set "Not a command: $2" "$@" ;;' _2 '*) set "Validation error ($1): $2" "$@"' _1 'esac' - [ "$_error" ] && _1 "$_error" '"$@" >&2 || exit $?' + [ "$_error" ] && _1 "$_error" '"$@" >&2 || return $?' _1 'echo "$1" >&2' - _1 'exit 1' + _1 'return 1' _0 '}' [ "$_help" ] && eval "shift 2; getoptions_help $1 $_help" ${3+'"$@"'} [ "$_def" ] && _0 "eval $_def \${1+'\"\$@\"'}; eval set -- \"\${$_rest}\"" - _0 '# Do not execute' # exit 1 + _0 '# Do not execute' # return 1 }