5

I want to know if there is any way to case on multiple variables at once, like this:

#/bin/bash
arr1=(1 2)
arr2=(3 4)
foo=1
bar=2

case $foo && $bar in
    ${arr1[@]})
        echo "variables equal array 1!";;
    ${arr2[@]})
        echo "variables equal array 2!";;
    *)
        echo "variables do not exactly equal any array!";;
esac

I don't want to put $foo and $bar in an array themselves, but I can if I absolutely have to.

3 Answers3

7

Assuming IFS hasn’t been changed from its default value:

case "$foo $bar" in
"${arr1[*]}") echo "variables equal array 1!" ;;
"${arr2[*]}") echo "variables equal array 2!" ;;
*) echo "variables do not exactly equal any array!" ;;
esac

This is only reliable if the separator (a space) isn’t present in the individual values stored in each array.

Stephen Kitt
  • 411,918
  • 54
  • 1,065
  • 1,164
  • 3
    And like always(?) when one tries to represent an array as a single string in a straightforward way, some restriction on possible values is needed. Here `foo=a; bar='b c'` gives the same string as `foo='a b'; bar=c`. – Kamil Maciorowski Jul 03 '22 at 22:09
  • This also requires the variables to be in the same order as the array elements. – Barmar Jul 04 '22 at 14:10
3

First off, I wouldn't worry about array variables.

Second, you can use any separator you like, provided it doesn't appear in your variables. I often use "/".

Something like:

case "$foo"/"$bar" in
(1/2) echo variables equal to first pattern\! ;;
(3/4) echo variables equal to second pattern\! ;;
(*) echo variables match no pattern. ;;
esac

This also then allows you to write wildcard entries fairly easily, like a line reading:

(1??/2??) echo variables are similar to the first pattern, but bigger\! ;;

Remember that the order of the patterns matters (first match wins), and that you want a separator that does not appear in your values.

Also, if you make your first pattern something like (*/*/*) you can test for your separator appearing in your values.

David G.
  • 1,314
  • 1
  • 4
  • 15
1

You could compare the output of typeset -p to do array equality comparison:

case $(a=("$foo" "$bar"); typeset -p a) in
  ("$(a=("${arr1[@]}"); typeset -p a)") ...;;
  ("$(a=("${arr2[@]}"); typeset -p a)") ...;;
esac

With zsh, you can join the elements after applying quoting to produce a deterministic string representation of the arrays which you can then compare as strings without having to run typeset -p in a subshell:

case "${(q)foo} ${(q)bar}" in
  (${(j[ ])${(q)arr1}}) ...;;
  (${(j[ ])${(q)arr2}}) ...;;
esac
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501