Bash – Using case and arrays together in bash

arraybashcasescriptingshell-script

Is it possible to check if a variable is contained inside an array using case? I would like to do something like

ARR=( opt1 opt2 opt3 );

case $1 in
    $ARR)
        echo "Option is contained in the array";
    *)
        echo "Option is not contained in the array";
esac

Best Answer

With ksh93, thanks to this bug, you can do:

IFS='|'
ARR=( opt1 opt2 opt3 )

IFS='|'
case $1 in
  (@("${ARR[*]}"))
    echo "Option is contained in the array";;
  (*)
    echo "Option is not contained in the array";;
esac

(I wouldn't rely on it as the bug might get fixed in the future).

With zsh, you could do:

case ${ARR[(Ie)$1]}
  (0)
    echo "Option is not contained in the array";;
  (*)
    echo "Option is contained in the array";;
esac

(though, you'd probably rather want to use if ((ARR[(Ie)$1])); then echo is present... here rather than a case construct).

${array[(I)pattern]} returns the index of the last element that matches the pattern in the array, or 0 otherwise. The e flag is for exact match (as opposed to pattern match).

With bash, ksh, yash, zsh, if you're ready to assume that $ARR and $1 don't contain a certain character like @, and that $ARR won't be empty, you can do:

IFS=@
case "@${ARR[*]}@" in
  (*"@$1@"*)
    echo "Option is contained in the array";;
  (*)
    echo "Option is not contained in the array";;
esac

With bash -O extglob, zsh -o kshglob -o globsubst, you could define a helper that builds a pattern based on the elements of the array:

arraypat() {
  awk '
    BEGIN{
      if (ARGC <= 1) print "!(*)"
      else {
        for (i = 1; i < ARGC; i++) {
          gsub(/[][|<>\\?*()]/, "[&]", ARGV[i])
          s = s sep ARGV[i]
          sep = "|"
        }
        print "@(" s ")"
      }
    }' "$@"
}

case $1 in
  ($(arraypat "${ARR[@]}"))
    echo "Option is contained in the array";;
  (*)
    echo "Option is not contained in the array";;
esac