Warnings about invalid options names that occur between valid option names

I have written the following bash piece, but need to implement something a bit more complicated.

The task is to detect an invalid option only if it happens between known options.

For instance, the following myfunc -v -w --vlt "Ubuntu" would print

Unknown option -w

But myfunc -v --vlt -w "Ubuntu" would not print any warning message because -w occurs after the known non-options arguments.

The code would only check option names starting with -. Thusly myfunc -v -w 5 --vlt "Ubuntu" would only report on the -w, disregarding any option values for warning messages.

 for i in "$@"; do
  case $i in
   ("-v"|"--verbosity"|"-u"|"--usage"|"-h"|"--help")
     printf '%s\n' "Option: $i"
     ;;
   ("--vlt"|"--blu"|"--grn"|"--ylw")
     printf '%s\n' "Option: $i"
     ;;
   ("--orn"|"--pur"|"--red"|"--wht")
     printf '%s\n' "Option: $i"
     ;;
   (*)
     printf '%s\n' "Invalid option | Argm: $i"
     ;;
  esac
 done

Solution 1:

In this case, it's probably best to switch strategy and shift your options because there is no way to get the list position in a for-loop, which makes it almost impossible to check options with a value:

#!/bin/bash

check_value(){
    ! [[ $1 = @(|-[!-]*|--[!-]*) ]]
}
expand_option(){
    local -n __a__=$2; local b=${1:1}
    [[ ! $b =~ [\ -] ]] && {
      [[ $b =~ ${b//?/(.)} ]] && { __a__=("${BASH_REMATCH[@]:1}"); __a__=("${__a__[@]/#/-}"); }
    } || return 1
}

declare -a a=() b=()
declare -A O=()

while (($# > 0)); do
    case "$1" in
    -[!-][!-]*)
        expand_option "$1" a || { \
        printf 'Invalid expansion: %s\n' "$1"; exit; }; set -- "${a[@]}" "${@:2}"
        continue
        ;;
     -[vuh])
        O[${1//-}]=1
        printf 'Option 1: %s\n' "$1"
        ;;
    -[!-]*)
        printf 'Invalid short option: %s\n' "$1"
        exit
        ;;
     --help)  ;&
     --usage) ;&
     --verbosity)
        O[${1//-}]=1
        printf 'Option 2: %s\n' "$1"
        ;;
     --vlt)
        check_value "$2" || { \
        printf "Invalid value: '%s'\n" "$2"; exit; }
        O[${1//-}]=$2
        printf 'Value for %s: %s\n' "$1" "$2"
        shift
        ;;
    --[!-]*)
        printf 'Invalid long option: %s\n' "$1"
        exit
        ;;
    --)
        b+=("${@:2}")
        break
        ;;
     *)
        b+=("$1")
        ;;
    esac
    shift
done

echo Final options '${O[@]}':
for k in "${!O[@]}"; do
    printf 'Key: %s, Value: %s\n' "$k" "${O[$k]}"
done

if ((${#b[@]} > 0)); then
    printf 'Unknown values: %s\n' "${b[*]}"
fi