(Similar to this, but in bash.)
I have an existing bash script that uses the builtin getopts to recognize -s
(flag only - no argument). I find I use that option every time, so I would like to make it default unless -s-
or +s
is specified on the command line. However, although ksh getopts
can handle +s
, I can't find that capability in the bash getopts
manual.
My current workarounds are:
s:
so I can recognize -s-
by $OPTARG
="-"
; or-s
with a different option, e.g., -d
(for "don't").However, #1 has the problem that it swallows the next argument if I accidentally specify -s
, and #2 has the problem that it uses a different switch letter than the one I already have in my muscle memory. I am hoping there might be a straightforward way to parse -s-
or +s
in bash.
+s
. It can handle optional arguments, so might be able to take -s-
. However, I've not used getopt(1)
before so would appreciate pointers on how not to shoot myself in the foot.+s
or -s-
, I'd love to see it.My current arg-parsing loop is very basic:
while getopts "nthps" opt ; do
case "$opt" in
<other cases cut>
(s) saw_s="yes"
;;
esac
done
shift $((OPTIND-1))
The sequence of negative flags (+abcdef +g
) differs from a normal sequence (-abcdef -g
) with just the plus character. So you can simply revert the flag value for +
prefix.
The second form of negative sequence is as simple. Just strip the last character (-
), and negate the normal flag value.
Example
The following script accepts all of the mentioned formats, e.g. -ns -n -s -n- -ns- +ns +n +s
.
arglist='ns'
while (( $# )); do
arg="$1"
# Parse -abcdef- (negative form of -abcdef) options
if [ "${arg:0:1}" = '-' -a "${arg#${arg%?}}" = '-' ]; then
flag_val=no
arg="${arg%?}" # -abcdef- becomes -abcdef
elif [ "${arg:0:1}" = '+' ]; then
flag_val=no
arg="${arg/#+/-}"
else
flag_val=yes
fi
# OPTIND is the index of the next argument to be processed.
# We are going to parse "$arg" from the beginning, so we need
# to reset it to 1 before calling getopts.
OPTIND=1
while getopts "$arglist" opt "$arg"; do
case "$opt" in
s) saw_s="$flag_val" ;;
n) saw_n="$flag_val" ;;
esac
done
shift
done
# Set default values
: ${saw_s:=yes}
: ${saw_n:=no}
printf "saw_s='%s'\nsaw_n='%s'\n" "$saw_s" "$saw_n"
Testing
$ ./pargs.sh
saw_s='yes'
saw_n='no'
$ ./pargs.sh -s
saw_s='yes'
saw_n='no'
$ ./pargs.sh +s
saw_s='no'
saw_n='no'
$ ./pargs.sh -s-
saw_s='no'
saw_n='no'
$ ./pargs.sh -s- +s -s
saw_s='yes'
saw_n='no'
$ ./pargs.sh -s +s
saw_s='no'
saw_n='no'
$ ./pargs.sh +s -s
saw_s='yes'
saw_n='no'
$ ./pargs.sh -s -s-
saw_s='no'
saw_n='no'
$ ./pargs.sh -sn
saw_s='yes'
saw_n='yes'
$ ./pargs.sh -sn -s-
saw_s='no'
saw_n='yes'
$ ./pargs.sh -sn +s
saw_s='no'
saw_n='yes'
$ ./pargs.sh +sn
saw_s='no'
saw_n='no'
$ ./pargs.sh -sn-
saw_s='no'
saw_n='no'
$ ./pargs.sh -sn- -n
saw_s='no'
saw_n='yes'