Last active
April 3, 2022 00:46
-
-
Save adrusi/e6bec921b12364a1dccaf6ff100f09fe to your computer and use it in GitHub Desktop.
Full-featured argument parsing in non-forking POSIX shell
This file contains 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
#!/bin/sh | |
source parse_args.inc.sh | |
arg_alias() { | |
case "$1" in | |
h) printf help;; | |
m) printf monitor;; | |
w) printf wait;; | |
r) printf rate; false;; | |
esac | |
} | |
arg_callback() { | |
case "$1" in | |
monitor) printf 'mode=monitor';; | |
wait) printf 'mode=wait';; | |
rate) printf 'packet_rate=%q' "$2"; false;; | |
esac | |
} | |
read -r -d '' usage <<USAGE | |
Usage: $(basename "$0") [-h|--help] [-m|--monitor] [-w|--wait] [-r RATE|--rate RATE] HOST... | |
--help Print this message. | |
--monitor Report changes to host status indefinitely. | |
--wait Exit when the host status changes. | |
--rate Specify how frequently to poll for host status. | |
USAGE | |
parse_args arg_alias arg_callback "$usage" "$@" | |
mode=${mode:-monitor} | |
packet_rate="${packet_rate:-1}" | |
case "$packet_rate" in | |
''|*[!0-9.]*) | |
printf '%s: invalid value for --rate: %s\n' "$(basename "$0")" "$packet_rate" >&2 | |
exit 1 | |
;; | |
esac |
This file contains 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
parse_args_program="$0" | |
parse_args_program_name="$(basename "$0")" | |
parse_args() { | |
if [ "$parse_args_args_already_parsed" ]; then | |
export -n parse_args_args_already_parsed | |
export -n parse_args_variable_names | |
parse_args_args="$(printf '%q ' "$@")" | |
eval "set -- $parse_arge_variable_name" | |
for parse_args_variable in "$@"; do | |
export -n $parse_args_variable | |
done | |
eval "set -- $parse_args_args" | |
return 0 | |
fi | |
parse_args_arg_alias="$1" | |
shift | |
parse_args_arg_callback="$1" | |
shift | |
parse_args_usage="$1" | |
shift | |
parse_args_normalized_args="" | |
while [ $# -gt 0 ]; do | |
case "$1" in | |
--?*=*) parse_args_normalized_args="$parse_args_normalized_args $(printf '%q %q' "${1%%=*}" "${1#*=}")";; | |
--*) parse_args_normalized_args="$parse_args_normalized_args $(printf '%q' "$1")";; | |
-?*) | |
parse_args_short_args="${1:1}" | |
while [ ${#parse_args_short_args} -gt 0 ]; do | |
parse_args_short_arg="${parse_args_short_args:0:1}" | |
parse_args_short_args="${parse_args_short_args:1}" | |
parse_args_expanded="$("$parse_args_arg_alias" "$parse_args_short_arg")" | |
parse_args_exit_code=$? | |
case "$parse_args_expanded" in | |
'') | |
printf '%s: unknown option: -%s\n' "$parse_args_program_name" "$parse_args_short_arg" >&2 | |
printf '%s\n' "$parse_args_usage" >&2 | |
exit 1 | |
;; | |
*[!A-Za-z_-]*) | |
printf 'parse_args: invalid alias expansion: %s\n' "$parse_args_expanded" >&2 | |
exit 1 | |
;; | |
*) | |
parse_args_normalized_args="$parse_args_normalized_args --$parse_args_expanded" | |
[ 0 -ne $parse_args_exit_code ] && break | |
;; | |
esac | |
done | |
if [ ${#parse_args_short_args} -gt 0 ]; then | |
parse_args_normalized_args="$parse_args_normalized_args $(printf '%q' "$parse_args_short_args")" | |
fi | |
;; | |
*) parse_args_normalized_args="$parse_args_normalized_args $(printf '%q' "$1")";; | |
esac | |
shift | |
done | |
eval "set -- $parse_args_normalized_args" | |
parse_args_variables="" | |
parse_args_positional_args="" | |
while [ $# -gt 0 ]; do | |
case "$1" in | |
--) | |
shift | |
parse_args_positional_args="$parse_args_positional_args $(printf '%q ' "$@")" | |
break | |
;; | |
--help) | |
printf '%s\n' "$parse_args_usage" | |
exit 0 | |
;; | |
--*) | |
parse_args_arg="$1" | |
shift | |
parse_args_callback_result="$("$parse_args_arg_callback" "${parse_args_arg:2}" "$@")" | |
parse_args_exit_code=$? | |
case "$parse_args_callback_result" in | |
'') | |
printf '%s: unknown option: %s\n' "$parse_args_program_name" "$parse_args_arg" >&2 | |
printf '%s\n' "$parse_args_usage" >&2 | |
exit 1 | |
;; | |
*) | |
if [ $# -lt $parse_args_exit_code ]; then | |
if [ 1 = $parse_args_exit_code ]; then | |
printf '%s: expected 1 parameter for option %s\n' "$parse_args_program_name" "$parse_args_arg" >&2 | |
else | |
printf '%s: expected %s parameter for option %s\n' "$parse_args_program_name" "$parse_args_exit_code" "$parse_args_arg" >&2 | |
fi | |
printf '%s\n' "$usage" >&2 | |
exit 1 | |
fi | |
parse_args_variables="$parse_args_variables $parse_args_callback_result" | |
shift $parse_args_exit_code | |
;; | |
esac | |
;; | |
*) | |
parse_args_positional_args="$parse_args_positional_args $(printf '%q' "$1")"; | |
shift | |
;; | |
esac | |
done | |
parse_args_variable_names="" | |
eval "set -- $parse_args_variables" | |
for parse_args_variable in "$@"; do | |
parse_args_variable_names="$parse_args_variable_names ${parse_args_variable%%=*}" | |
done | |
eval "parse_args_args_already_parsed=1 parse_args_variable_names=$(printf '%q' "$parse_args_variable_names") $parse_args_variables exec $(printf '%q' "$parse_args_program") $parse_args_positional_args" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment