I'm writing a bash shell script that can use the flags -l
-u
and -w [argument]
I have the following (simplified) code:
while getopts ":ulw:" arg; do
case "$arg" in
u)
U=1
;;
l)
L=1
;;
w)
W=1
VALUE="${OPTARG}"
;;
esac
done
when I run my script with -w42 -l
it works like it should. If I use -lw42
it also works but when I use -w42l
, it thinks 42l
is the argument (instead of just 42
) and it makes the VALUE
variable = 42l
and ignores the -l
option.
How can I make the script work for both -w42 -l
, -lw42
and -w42l
?
EDIT: to clear things up, I know this isn't the way it's supposed to work but my college requires me to make it work this way
CodePudding user response:
On Standards-Compliance
What you are trying is not supposed to work in the first place.
POSIX utility syntax guideline #5 states:
One or more options without option-arguments, followed by at most one option that takes an option-argument, should be accepted when grouped behind one '-' delimiter.
So, the option taking an option-argument (-w
in this case) is only allowed to be the last one in a group started by a single -
.
On Making It Work Anyhow
If you can't deal with standard-compliant behavior, you can't use getopts
, so you need to write your own logic. One way to do that might look like the following:
#!/usr/bin/env bash
# ^^^^- note bash, not sh; the below code uses non-POSIX extensions
while (( $# )) && [[ $1 = -* ]]; do
arg=${1#-}; shift
while [[ $arg ]]; do
case $arg in
l*) flag_l=1; arg=${arg#l};;
u*) flag_u=1; arg=${arg#u};;
w*)
flag_w=1
rest=${arg#w}
if [[ -z $rest ]]; then
arg=$1; shift; rest=$arg
fi
if [[ $rest =~ ^([[:digit:]] )(.*) ]]; then
w_value=${BASH_REMATCH[1]}
arg=${BASH_REMATCH[2]}
else
echo "ERROR: -w not followed with a number" >&2
exit 1
fi
;;
*) echo "Unrecognized flag: $arg" >&2; exit 1;;
esac
done
done
echo "After parsing:"
echo "flag_w = ${flag_w:-0}"
echo "flag_l = ${flag_l:-0}"
echo "flag_u = ${flag_u:-0}"
echo "w_value = ${w_value:-0}"
See this running in the online interpreter at https://ideone.com/eDrlHd