Skip to content

Instantly share code, notes, and snippets.

@dberstein
Last active June 17, 2019 11:40
Show Gist options
  • Save dberstein/2795cde8eb8a99f1d1951f859ee343bd to your computer and use it in GitHub Desktop.
Save dberstein/2795cde8eb8a99f1d1951f859ee343bd to your computer and use it in GitHub Desktop.
#!/usr/bin/env sh
##########################################
# args - shell arguments parsing library #
##########################################
# Usage:
# - function "args_parse" with the parameters to parse, response will be a temp filename used to store the
# parsed data (see "Arguments parsing" and "Cleanup").
# Example: ARGS=$(args_parse "$@")
#
# - function "args_flag" with arguments parsing filename and argument name (see "Arguments querying").
# Example: FLAG_VALUE=$(args_flag "${ARGS}" "flag-name")
#
# Arguments parsing:
#
# Long flag arguments:
# "--<name>=[<value>]": continuos text between double dash and equal sign is flag name
# "--<name> <value>": same as above but value os obtainer from next argument
#
# Short flag arguments:
# "-<n><value>": single dash can have a single character name, its value can be adjacent to it
# (please note that "-n<n>=value" is parsed as "n" equals to "=value") ...
# "-<n> <value>": .. or can be the next argument
#
# Switch arguments (value-less):
# Some arguments usually not need a value (ie. -h or --help), this is supported using the env var
# "ARG_SWITCH_FLAGS", whose content must be a string listing the flags that should be trated as value-less
# switches. Short flag switches aer denoted by their single character name, long flag switches by a dash
# followed by their name. Example: export ARG_SWITCH_FLAGS="h -help" would allow both "-h" and "--help" to
# be value-less flags. An error is throw if a value is tried to be assigned. If flag is used it'll have the fixed
# value of "1".
#
# "-<n>" or "--<name>": If flag name is registered as "ARG_SWITCH_FLAGS" then its value will be set to "1"
#
# Positional arguments:
# All text starting from the first non-flag argument or the standard marker "--" are considered positional
# parameters whose value can be queried using key "--"
#
# "--": any argument following this is treated as a positional argument
#
# If an argument's prefix is of the form "--<name>",
#
# Argument querying:
# Call function args_flag with the arguments filename and the argument name (long flags must be prefixed by a dash).
# The response will be all arguments that match the given flag name.
#
# Positional arguments:
# Positional arguments can be obtained by querying argument name "--"
#
# Cleanup:
# In order to cleanup the temporary arguments file, its recommended to use a trap. For example:
# trap '[ -f "${ARGS}" ] && rm "${ARGS}"' INT TERM EXIT && \
# ARGS=$(ARG_SWITCH_FLAGS="h -help" args_parse "$@")
#
args_echo_error () {
>&2 echo "$@"
}
arg_parse_is_switch () {
local SWITCH
if [ -n "$1" ]; then
SWITCH="$(echo "$3" | sed 's/-/\\-/')" && \
echo "$1" | egrep -q '(^|\s)'${SWITCH}'(\s|$)' && \
return $?
fi
return 1
}
arg_parse_switch_invalid_error () {
[ -f "${FILE}" ] && rm "${FILE}"
args_echo_error "ERROR argument does not accept a value: -${1}"
return 2
}
arg_parse_error_switch () {
echo "$1" | egrep -q '(^|\s)'$(echo "$2" | sed 's/-/\\-/')'(\s|$)' && \
arg_parse_switch_invalid "${NAME}" && \
return $?
return 0
}
args_parse () {
local FILE; local NAME; local VALUE; local SWITCH_STATUS
FILE="$(mktemp)"
# while we have arguments ...
while [ $# -gt 0 ]; do
VALUE=""; SWITCH_STATUS=0
ARG="$1" && shift
# stop parsing, treat remainder as positional arguments
if [ '--' = "${ARG}" ]; then
echo "--=${@}" >> "${FILE}"; break
# long argument: value attached to argument after "=" sign
elif echo "${ARG}" | egrep -q '^--[^=]+=.*$'; then
NAME=$(echo "${ARG}" | sed 's/^--\([^=]*\)=.*$/-\1/')
if arg_parse_is_switch "${ARG_SWITCH_FLAGS}" "${ARG}" "${NAME}"; then
arg_parse_switch_invalid_error "${NAME}"
return $?
fi
VALUE=$(echo "${ARG}" | sed 's/^--.*=\(.*\)$/\1/')
# long argument: next arg has value or is switch flag
elif echo "${ARG}" | egrep -q '^--[^=]+$'; then
NAME=$(echo "${ARG}" | sed 's/^--/-/')
# switch flag cannot have value
if arg_parse_is_switch "${ARG_SWITCH_FLAGS}" "${ARG}" "${NAME}"; then
VALUE="1"
else
[ $# -gt 0 ] && VALUE="$1" && shift
fi
# short argument: value attached to argument
elif echo "${ARG}" | egrep -q '^-[^-].'; then
NAME=$(echo "${ARG}" | sed 's/^-\([^-]\).*$/\1/')
if arg_parse_is_switch "${ARG_SWITCH_FLAGS}" "${ARG}" "${NAME}"; then
arg_parse_switch_invalid_error "${NAME}"
return $?
fi
VALUE=$(echo "${ARG}" | sed 's/^-[^-]\(.*\)/\1/')
# short argument: next arg has value or is switch flag
elif echo "${ARG}" | egrep -q '^-[^-]$'; then
NAME=$(echo "${ARG}" | sed 's/^-\([^-]\)$/\1/')
if arg_parse_is_switch "${ARG_SWITCH_FLAGS}" "${ARG}" "${NAME}"; then
VALUE="1"
else
[ $# -gt 0 ] && VALUE="$1" && shift
fi
# non-flag argument, stop parsing, treat remainder as positional arguments
else
echo "--=${ARG}" >> "${FILE}"; echo "--=${@}" >> "${FILE}"; break
fi
echo "${NAME}=${VALUE}" >> "${FILE}"
done && echo "${FILE}"
}
args_flag () {
# Returns flag arguments for flag $2.
# $1 (string) args filename.
# $2 (string) flag, long flags shou;d be prefixed by a dash.
grep "^$2=" "$1" | cut -d= -f2-
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment