Skip to content

Instantly share code, notes, and snippets.

@GongT
Created January 27, 2020 02:03
Show Gist options
  • Save GongT/a523dd359abc4ce25d6c60f7ffd59628 to your computer and use it in GitHub Desktop.
Save GongT/a523dd359abc4ce25d6c60f7ffd59628 to your computer and use it in GitHub Desktop.
bash argument options parser (BASH>=4.2)
declare -a _ARG_GETOPT_LONG
declare -a _ARG_GETOPT_SHORT
declare -A _ARG_COMMENT
declare -A _ARG_INPUT
declare -A _ARG_OUTPUT
declare -A _ARG_RESULT
declare -A _ARG_REQUIRE
function arg_string() {
if [[ "$1" == '+' ]]; then
shift
_ARG_REQUIRE[$1]=yes
fi
local VAR_NAME=$1 SHORT LONG IN=''
shift
_arg_parse_name $1
shift
_ARG_COMMENT[$VAR_NAME]="$*"
declare $VAR_NAME=""
[[ -n "$LONG" ]] && {
IN+="/--$LONG"
_ARG_GETOPT_LONG+=("$LONG:")
_ARG_OUTPUT["--$LONG"]=$VAR_NAME
}
[[ -n "$SHORT" ]] && {
IN+="/-$SHORT"
_ARG_GETOPT_SHORT+=("$SHORT:")
_ARG_OUTPUT["-$SHORT"]=$VAR_NAME
}
_ARG_RESULT[$VAR_NAME]=""
_ARG_INPUT[$VAR_NAME]="${IN:1} <$VAR_NAME>"
}
function arg_flag() {
local VAR_NAME=$1 SHORT LONG IN=''
shift
_arg_parse_name $1
shift
_ARG_COMMENT[$VAR_NAME]="$*"
declare $VAR_NAME=""
[[ -n "$LONG" ]] && {
IN+="/--$LONG"
_ARG_GETOPT_LONG+=("$LONG")
_ARG_OUTPUT["--$LONG"]=$VAR_NAME
}
[[ -n "$SHORT" ]] && {
IN+="/-$SHORT"
_ARG_GETOPT_SHORT+=("$SHORT")
_ARG_OUTPUT["-$SHORT"]=$VAR_NAME
}
_ARG_RESULT[$VAR_NAME]=""
_ARG_INPUT[$VAR_NAME]="${IN:1}"
}
function arg_finish() {
local ARGS=(--name "$0")
if [[ ${#_ARG_GETOPT_LONG} -gt 0 ]]; then
local S=''
for I in "${_ARG_GETOPT_LONG[@]}" ; do
S+=",$I"
done
S=${S:1}
ARGS+=(--longoptions "$S")
fi
if [[ ${#_ARG_GETOPT_SHORT} -gt 0 ]]; then
local S='+'
for I in "${_ARG_GETOPT_SHORT[@]}" ; do
S+="$I"
done
ARGS+=(--options "$S")
fi
# echo "${ARGS[@]} -- $@"
eval _arg_set $(getopt "${ARGS[@]}" -- "$@" || arg_usage)
}
function arg_usage() {
echo -e "\e[38;5;14mUsage: $0 <options>\e[0m" >&2
local K
{
for K in "${!_ARG_INPUT[@]}" ; do
echo -e " \e[2m${_ARG_INPUT[$K]}\e[0m|${_ARG_COMMENT[$K]}"
done
} | column -t -c "${COLUMNS-80}" -s '|' >&2
exit 1
}
function _arg_parse_name() {
local NAME=$1
local A=${NAME%%/*} B=${NAME##*/}
if [[ "$A" == "$B" ]] ; then
LONG=$A
SHORT=""
elif [[ ${#A} -gt ${#B} ]] ; then
LONG=$A
SHORT=$B
else
LONG=$B
SHORT=$A
fi
if [[ -z "$SHORT" ]] && [[ ${#LONG} -eq 1 ]] ; then
SHORT=$LONG
LONG=
fi
if ! echo "$LONG$SHORT" | grep -qE "^[0-9a-z-]+$" ; then
die "Invalid argument define: $NAME (LONG=$LONG, SHORT=$SHORT)"
fi
if [[ ${#SHORT} -gt 1 ]]; then
die "Short argument only allow single char (LONG=$LONG, SHORT=$SHORT)"
fi
}
function _arg_set() {
local VAR_NAME
while [[ "$1" != "--" ]]; do
VAR_NAME=${_ARG_OUTPUT[$1]}
if [[ "${2:0:1}" == "-" ]]; then
_ARG_RESULT[$VAR_NAME]=yes
else
_ARG_RESULT[$VAR_NAME]=$2
shift
fi
shift
done
shift
if [[ $# -gt 0 ]]; then
die "Unknown argument $1"
fi
for VAR_NAME in "${!_ARG_RESULT[@]}" ; do
if [[ -z "${_ARG_RESULT[$VAR_NAME]}" ]] && [[ -n "${_ARG_REQUIRE[$VAR_NAME]-}" ]]; then
die "Argument '${_ARG_INPUT[$VAR_NAME]}' is required"
fi
declare -rg "$VAR_NAME=${_ARG_RESULT[$VAR_NAME]}"
done
for VAR_NAME in "${!_ARG_RESULT[@]}" ; do
echo -e "\e[2m$VAR_NAME=${_ARG_RESULT[$VAR_NAME]}\e[0m" >&2
done
}
set -Eeuo pipefail
source common/functions.sh
arg_string + USERNAME u/username "login username"
arg_string PASSWORD p/password "login password"
arg_flag REMEMBER remember "remember login"
arg_finish "$@"
echo $PASSWORD
PASSWORD=123 # fail
echo $PASSWORD
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment