Mangle long-option names to allow dashes

Fixes problem where long options with dashes, like `--no-color` were
 broken. This was due to the fact that bash variable names must match
 `[_a-zA-Z][_0-9a-zA-Z]*` and the usage parsing and option handling was
 trying to create variables with dashes in their names. Short of
 employing Bash4 associative arrays, "name mangling" seemed like the
 best solution to this problem.

Solution: map dashes to underscores when creating bash variables in the
script that correspond to long option flags. The downside of this is
that `--no_color` and `--no-color` will collide, but users expecting to
use options that are identical except one has an underscore and the
other has a dash deserve the ensuing confusion.
This commit is contained in:
Izaak Beekman 2016-02-19 23:25:09 -05:00
parent db690268ea
commit a86d8d82b1
No known key found for this signature in database
GPG Key ID: CB21118C92A64702

21
main.sh
View File

@ -56,6 +56,8 @@ read -r -d '' usage <<-'EOF' || true # exits non-zero when EOF encountered
-v Enable verbose mode, print script as it is executed
-d --debug Enables debug mode
-h --help This page
-n --no-color Disable color output
-1 --one Do just one thing
EOF
# Set magic variables for current file and its directory.
@ -120,14 +122,15 @@ trap cleanup_before_exit EXIT
# Translate usage string -> getopts arguments, and set $arg_<flag> defaults
while read line; do
# fetch single character version of option sting
# fetch single character version of option string
opt="$(echo "${line}" |awk '{print $1}' |sed -e 's#^-##')"
# fetch long version if present
long_opt="$(echo "${line}" |awk '/\-\-/ {print $2}' |sed -e 's#^--##')"
long_opt_mangled="$(sed 's#-#_#g' <<< $long_opt)"
# map long name back to short name
varname="short_opt_${long_opt}"
varname="short_opt_${long_opt_mangled}"
eval "${varname}=\"${opt}\""
# check if option takes an argument
@ -157,6 +160,9 @@ opts="${opts}-:"
# Reset in case getopts has been used previously in the shell.
OPTIND=1
# start parsing command line
set +o nounset # unexpected arguments will cause unbound variables
# to be dereferenced
# Overwrite $arg_<flag> defaults with the actual CLI options
while getopts "${opts}" opt; do
[ "${opt}" = "?" ] && help "Invalid use of script: ${@} "
@ -166,13 +172,15 @@ while getopts "${opts}" opt; do
if [[ "${OPTARG}" =~ .*=.* ]]; then
# --key=value format
long=${OPTARG/=*/}
long_mangled="$(sed 's#-#_#g' <<< $long)"
# Set opt to the short option corresponding to the long option
eval "opt=\"\${short_opt_${long}}\""
eval "opt=\"\${short_opt_${long_mangled}}\""
OPTARG=${OPTARG#*=}
else
# --key value format
# Map long name to short version of option
eval "opt=\"\${short_opt_${OPTARG}}\""
long_mangled="$(sed 's#-#_#g' <<< $OPTARG)"
eval "opt=\"\${short_opt_${long_mangled}}\""
# Only assign OPTARG if option takes an argument
eval "OPTARG=\"\${@:OPTIND:\${has_arg_${opt}}}\""
# shift over the argument if argument is expected
@ -183,14 +191,15 @@ while getopts "${opts}" opt; do
varname="arg_${opt:0:1}"
default="${!varname}"
value="${OPTARG:-}"
if [ -z "${OPTARG:-}" ] && [ "${default}" = "0" ]; then
value="${OPTARG}"
if [ -z "${OPTARG}" ] && [ "${default}" = "0" ]; then
value="1"
fi
eval "${varname}=\"${value}\""
debug "cli arg ${varname} = ($default) -> ${!varname}"
done
set -o nounset # no more unbound variable references expected
shift $((OPTIND-1))