Command-line scripts are easier to reuse when named options replace a fragile positional-only interface. The Zsh getopts builtin reads short options one at a time while leaving the remaining arguments available to the rest of the script.
Each successful call to getopts writes an option letter to the loop variable, stores its value in OPTARG, and advances OPTIND to the next unparsed argument. A colon after an option letter means that option requires a value, so r: accepts both -r eu-west-1 and -reu-west-1.
Silent mode keeps option errors under the script's control instead of mixing builtin diagnostics with its interface. The finished parser accepts -n for dry-run mode, -r for a region override, and an environment name after the options; a leading colon in :nr: routes missing values and unknown options to explicit error branches.
Related: How to use conditionals in Zsh
Related: How to use a case statement in Zsh
Related: How to debug a Zsh script
Related: Parse command-line options in Bash with getopts
#!/usr/bin/env zsh emulate -L zsh setopt err_exit no_unset region="us-east-1" dry_run=0
emulate -L zsh gives the script local Zsh option settings, while no_unset stops accidental reads of undefined parameters.
while getopts ":nr:" opt; do
case $opt in
n)
dry_run=1
;;
r)
region=$OPTARG
;;
esac
done
Each successful call to getopts supplies one option letter. The -n branch changes a flag, and the -r branch reads its required value from OPTARG.
:)
print -u2 -- "missing value for -$OPTARG"
exit 2
;;
\?)
print -u2 -- "unknown option: -$OPTARG"
exit 2
;;
The leading colon in :nr: sets the loop variable to : for a missing value and ? for an unknown option. In both cases, OPTARG contains the affected option letter.
shift $(( OPTIND - 1 ))
environment=${1-}
if [[ -z $environment ]]; then
print -u2 -- 'usage: deploy-options.zsh [-n] [-r region] environment'
exit 2
fi
OPTIND points to the first argument that getopts did not consume. After the shift, $1 is the environment name rather than an option or its value.
print -r -- "environment=$environment" print -r -- "region=$region" print -r -- "dry_run=$dry_run"
#!/usr/bin/env zsh
emulate -L zsh
setopt err_exit no_unset
region="us-east-1"
dry_run=0
while getopts ":nr:" opt; do
case $opt in
n)
dry_run=1
;;
r)
region=$OPTARG
;;
:)
print -u2 -- "missing value for -$OPTARG"
exit 2
;;
\?)
print -u2 -- "unknown option: -$OPTARG"
exit 2
;;
esac
done
shift $(( OPTIND - 1 ))
environment=${1-}
if [[ -z $environment ]]; then
print -u2 -- 'usage: deploy-options.zsh [-n] [-r region] environment'
exit 2
fi
print -r -- "environment=$environment"
print -r -- "region=$region"
print -r -- "dry_run=$dry_run"
$ zsh -n deploy-options.zsh
No output means Zsh parsed the file without finding a syntax error.
$ zsh deploy-options.zsh -n -r eu-west-1 staging environment=staging region=eu-west-1 dry_run=1
$ zsh deploy-options.zsh -r missing value for -r
$ zsh deploy-options.zsh -x staging unknown option: -x
$ zsh deploy-options.zsh -n -r eu-west-1 staging environment=staging region=eu-west-1 dry_run=1