Home > Software engineering >  Warnings for invalid options
Warnings for invalid options

Time:12-11

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
  • Related