I have a bash function that allows the user to set options and parameters.
heading [OPTIONS] PARAM...
I have specified the following options
-V, --version, -v, --verbosity, -u, --usage, -h, --help
--vlt, --blu, --grn, --ylw, --orn, --pur, --red, --wht, -mSTY
I want to issue a warning for invalid options provided the invalid option is surrounded by available function options.
This is good
heading --vlt -m1 PARAMS
This is also good, because -adv comes after all available options
heading --vlt -m1 -adv Din
But this requires a warning, because --adv appears before available option -m1
heading --vlt --adv -m1 Din
The function is listed here
copt ()
{
while (( $# > 0 )); do
case $1 in
("-V"|"--version") shift ; return 0 ;;
("-v"|"--verbosity") vb=1 ; shift ;;
("-u"|"--usage") usg=1 ; shift ; return 0 ;;
("-h"|"--help") shift ; return 0 ;;
("--vlt") vlt=$(tput setaf 57) ; shift ;;
("--blu") blu=$(tput setaf 12) ; shift ;;
("--grn") grn=$(tput setaf 2) ; shift ;;
("--ylw") ylw=$(tput setaf 3) ; shift ;;
("--orn") orn=$(tput setaf 166) ; shift ;;
("--pur") pur=$(tput setaf 93) ; shift ;;
("--red") red=$(tput setaf 1) ; shift ;;
("--wht") wht=$(tput setaf 7) ; shift ;;
("-m"*) sty="${1#-m}" ; shift ;;
("--") shift ; break ;;
(*) break ;;
esac
done
}
CodePudding user response:
By looping over the arguments, you are basically forcing yourself to accept anything after the first non-option argument, even if it's not a supported option. What you can do is loop over the arguments twice, or loop over the remaining arguments after the last valid option, and warn if anything after the first non-option argument is an option.
Rough draft:
copt ()
{
while (( $# > 0 )); do
case $1 in
("-V"|"--version") shift ; return 0 ;;
("-v"|"--verbosity") vb=1 ; shift ;;
("-u"|"--usage") usg=1 ; shift ; return 0 ;;
("-h"|"--help") shift ; return 0 ;;
("--vlt") vlt=$(tput setaf 57) ; shift ;;
("--blu") blu=$(tput setaf 12) ; shift ;;
("--grn") grn=$(tput setaf 2) ; shift ;;
("--ylw") ylw=$(tput setaf 3) ; shift ;;
("--orn") orn=$(tput setaf 166) ; shift ;;
("--pur") pur=$(tput setaf 93) ; shift ;;
("--red") red=$(tput setaf 1) ; shift ;;
("--wht") wht=$(tput setaf 7) ; shift ;;
("-m"*) sty="${1#-m}" ; shift ;;
("--") shift ; break ;;
(*) break ;;
esac
done
local arg validopt invalidopt
for arg; do
case $arg in
"-V"|"--version" \
"-v"|"--verbosity" \
"-u"|"--usage" \
"-h"|"--help" \
"--vlt" \
"--blu" \
"--grn" \
"--ylw" \
"--orn" \
"--pur" \
"--red" \
"--wht" \
"-m"*) validopt=$arg;;
"-"*) invalidopt=$arg;;
esac
if [[ $validopt && $invalidopt ]]; then
echo "copt: cannot have invalid option $invalidopt before $validopt" >&2
exit 1 # or whatever
fi
done
}
This is rather repetitive and clumsy, and I would really not recommend deviating from de facto option processing conventions like this. Having the function abort for the first invalid option argument is much more predictable and usable, especially since you already support --
to signal the end of options.
CodePudding user response:
I might create an associative array mapping unexpected arguments to where they occur in the args list and process them after the initial loop like (untested pseudo-code):
declare -A unxArgs
while whatever; do
(( argCnt ))
gotUnxArg=0
case $arg in
--foo) do good arg stuff ;;
--bar) do good arg stuff ;;
*) gotUnxArg=1 ;;
esac
if (( gotUnxArg == 0 )); then
lastGoodArgPos="$argCnt"
else
unxArgs["$arg"]="$argCnt"
fi
done
for unxArg in "${!unxArgs[@]}"; do
argPos="${unxArgs[$unxArg]}"
if (( argPos < lastGoodArgPos )); then
echo "the sky is falling" >&2
fi
done