2

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
  • 1
    Are you mandated to use case? – Inian Dec 15 '17 at 8:57
  • Possible duplicate of How do I test if an item is in a bash array? – muru Dec 15 '17 at 12:21
  • @muru, I'd say it's different here because of the case requirement. Presumably in the end the OP wants to add more cases as in case $1 in $ARR1)... $ARR2)... foo) bar)...; esac – Stéphane Chazelas Dec 15 '17 at 13:10
4

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
3

Not really in a compact and easy to use way. Remember that $ARR will expand to only the first element of the array, opt1 in your example.

You could use "${ARR[@]}", but using your data this would give a false positive for the string 1 opt.

With more recent versions of bash, you could consider using an associative array:

declare -A arr
arr=( [opt1]=1 [opt2]=1 [opt3]=1 )

if [[ "${arr[$1]}" -eq 1 ]]; then
   # $1 is a key in arr
else
   # is not
fi
  • Or to get full use of the associative array, i.e. use arbitrary values, not just 1, test with something like [[ "${arr[$1]+x}" ]] (it's shorter, too!) – ilkkachu Dec 15 '17 at 10:44
  • @ilkkachu Yes, or [[ -n "${arr[$1]}" ]]. – Kusalananda Dec 15 '17 at 10:49
1

Why would you want to do it with case? It's meant for string pattern matching, not per-element matching.

Frankly, if you need the "contains" test often and want to make it short because of that, just put the hard part in a function instead of using ugly workarounds:

#!/bin/bash
ARR=( foo bar doo );

contains() {
        typeset _x;
        typeset -n _A="$1"
        for _x in "${_A[@]}" ; do
                [ "$_x" = "$2" ] && return 0
        done
        return 1
}

if contains ARR "$1" ; then
        echo "\"$1\" is contained in ARR"
else
        echo "\"$1\" not contained in ARR"
fi

(That should also work in ksh)

  • OP probably wants a builtin for the presumed speed and efficiency. – RonJohn Dec 15 '17 at 13:26
  • @RonJohn, everything that function does, is built in to Bash. As for speed, it's hard to say, we'd need to test, but for huge data sets, associative arrays would probably be faster (than either the loop, or a case), since key lookups are presumably O(1). But for huge data, you shouldn't use the shell anyway, and for small data, minor performance differences don't matter. – ilkkachu Dec 15 '17 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.