Skip to content

Instantly share code, notes, and snippets.

@mattmc3
Last active May 15, 2025 21:02
Show Gist options
  • Save mattmc3/64bc4da0235b757fb6ec3f066c2b9f0e to your computer and use it in GitHub Desktop.
Save mattmc3/64bc4da0235b757fb6ec3f066c2b9f0e to your computer and use it in GitHub Desktop.
Bash getopts - normalize flags
#!/usr/bin/env bash
# Normalize --long-flags to -s short ones for use with getopts.
normalize_flags() {
local shortopt longopt
local -a args=()
local -A spec=()
# Process option definitions until we hit the -- separator
while [[ "$#" -gt 0 && "$1" != -- ]]; do
# Split the "short/long" option spec and save the mapping
shortopt="${1%%/*}"
longopt="${1#*/}"
spec["--${longopt}"]="-${shortopt}"
shift
done
# Check for the required -- separator
if [[ "$1" != -- ]]; then
printf >&2 'normalize_args: %s\n' "Missing required -- separator"
return 1
fi
shift # Skip the -- separator
# Handle longopt to shortopt conversion,
# respecting anything after an optional --
while [[ "$#" -gt 0 ]]; do
if [[ -n "${spec["$1"]}" ]]; then
args+=("${spec["$1"]}")
elif [[ "$1" == -- ]]; then
args+=("$@")
break
else
args+=("$1")
fi
shift
done
# Return new args via the reply array
typeset -ga reply=("${args[@]}")
}
### Usage
# call normalize_flags defining -s short to --long option mappings where appropriate
normalize_flags 'a/any' 'd/do-something' 'h/help' 'v/version' 'x/extra' -- "$@"
set -- "${reply[@]}"
unset reply
# Use getopts confidently, now that all options are short
while getopts ":adhvx:" opt; do
case "$opt" in
a) echo "Option A" ;;
h) echo "Help" ;;
d) echo "Option D" ;;
v) echo "Version" ;;
z) echo "Extra: $OPTARG" ;;
\?) echo "Invalid option"; return 1 ;;
esac
done
# Ensure OPTIND is at least 1 before shifting
[[ $OPTIND -gt 1 ]] && shift $((OPTIND - 1))
unset OPTARG OPTIND OPTOPT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment