The generated option parser code contains workarounds for several shell bugs. The reason why those codes are needed is not easy to understand and is explained here. If you do not need these workarounds, you can remove them manually.
# e.g.
eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
eval '[ ${OPTARG+x} ] &&:' && OPTARG=1 || OPTARG=-1
$1*) OPTARG="$OPTARG --flag"
VERBOSE="$((${VERBOSE:-0}+$OPTARG))"
eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
# Equivalent to the following
set -- "${OPTARG%%\=*}" "${OPTARG#*\=}" "$@"
This is a well-known workaround the problem of using set -u
for some shells.
See What does ${1+"$@"}
mean.
Oh wait, there's another meaning It's also a workaround for a problem in some shells if you want to use the flag u ("error upon using empty variables") and
"$@"
(or$@
,"$*"
,$*
) is used without arguments. Example:$ shell -cu 'echo "$@"; echo not reached' @: parameter not set $ shell -cu 'echo ${1+"$@"}; echo ok' okAt least these shells need that workaround:
- bash-4.0.0 ... -4.0.27
- dash-0.4.6 ... -0.4.18
- all ksh88
- ksh93 until release t+20090501
- pdksh-5.1.3, -5.2.14
- mksh before R39 (as pdksh descendant)
- posh < 0.10 (as pdksh descendant)
- NetBSD 2 ff. /bin/sh
- all traditional Bourne shells
NOTE: In NetBSD ksh on NetBSD 9.0 and posh 0.14.1, this problem still exists.
However, this workaround does not work properly with old zsh 3.x, so we are using a modified version.
$ echo $ZSH_VERSION
3.1.9
$ set -- a b c; printf '[%s] ' ${1+"$@"}; echo
[a b c]
# correct: [a] [b] [c]
Modified version
eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
# if $1 is present, then
=> eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' '"$@"'
=> set -- "${OPTARG%%\=*}" "${OPTARG#*\=}" "$@"
# if $1 is not present, then
=> eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"'
=> set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"
eval '[ ${OPTARG+x} ]' && OPTARG=1 || OPTARG=-1
This is a workaround for ksh 93u+. The code without the workaround is the following.
[ ${OPTARG+x} ] && OPTARG=1 || OPTARG=-1
The reproduction code is as follows. The eval
avoids this problem.
#!/usr/bin/ksh
set -eu 1 2
VAR='var'
while [ $# -gt 0 ]; do
[ $# -eq 1 ] && unset VAR # unset VAR in the last loop
if [ "${VAR+x}" ]; then # if variable VAR is set
# The last loop will be executed even though the variable VAR is not set
echo "$VAR" # => [error] VAR: parameter not set
fi
shift
done
eval '[ ${OPTARG+x} ] &&:' && OPTARG=1 || OPTARG=-1
This &&:
is a workaround for OpenBSD ksh.
The reproduction code is as follows.
set -eu
eval "[ ]" && v=1 || v=2 # it should not exit with failure, but it does
eval "[ ] &&:" && v=1 || v=2 # it works as expected
The $1
in this code should be enclosed in double quotes. If $1
contains a pattern string, it will cause incorrect behavior.
set -- "???"
case foo in
"$1"*) echo "it doesn't match" ;; # correct
$1*) echo "it matches" ;; # wrong
esac
However, posh 0.10.2, 0.12.6 doesn't work properly when variables are enclosed in double quotes.
#!/usr/bin/posh
set -- "foo"
case foo in
"$1"*) echo "ok" ;;
*) echo "not ok" >&2; exit 1 ;; # posh 0.10.2, 0.12.6
esac
Therefore, it cannot be enclosed in double quotes, but it would be in trouble
if $1
contains a pattern, so we check it beforehand.
case $1 in (*[!a-zA-Z0-9_-]*) break; esac
This workaround has a very limited number of affected shells and limits the characters that can be used as long options, so it may be removed in the future.
There is no need to use $
to refer to simple variables in arithmetic expressions.
VERBOSE="$((${VERBOSE:-0}+OPTARG))"
However, older shells (e.g. dash 0.5.2) require $
. The affected shells are
limited and quite old, but we use $
because it doesn't cause any problems.