How can I use a variable as a case condition?

The bash manual states:

case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac

Each pattern examined is expanded using tilde expansion, parameter and variable expansion, arithmetic substitution, command substitution, and process substitution.

No «Pathname expansion»

Thus: a pattern is NOT expanded with «Pathname expansion».

Therefore: a pattern could NOT contain "|" inside. Only: two patterns could be joined with the "|".

This works:

s1="foo"; s2="bar"    # or even s1="*foo*"; s2="*bar*"

read choice
case $choice in
    $s1|$s2 )     echo "Two val choice $choice"; ;;  # not "$s1"|"$s2"
    * )           echo "A Bad  choice! $choice"; ;;
esac

Using « Extended Globbing »

However, word is matched with pattern using « Pathname Expansion » rules.
And « Extended Globbing » here, here and, here allows the use of alternating ("|") patterns.

This also work:

shopt -s extglob

string='@(foo|bar)'

read choice
    case $choice in
        $string )      printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )      printf 'Two val choice %-20s' "$choice"; ;;
        *)             printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
echo

String content

The next test script shows that the pattern that match all lines that contain either foo or bar anywhere is '*$(foo|bar)*' or the two variables $s1=*foo* and $s2=*bar*


Testing script:

shopt -s extglob    # comment out this line to test unset extglob.
shopt -p extglob

s1="*foo*"; s2="*bar*"

string="*foo*"
string="*foo*|*bar*"
string='@(*foo*|*bar)'
string='*@(foo|bar)*'
printf "%s\n" "$string"

while IFS= read -r choice; do
    case $choice in
        "$s1"|"$s2" )   printf 'A first choice %-20s' "$choice"; ;;&
        $string )   printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )   printf 'Two val choice %-20s' "$choice"; ;;
        *)      printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
    echo
done <<-\_several_strings_
f
b
foo
bar
*foo*
*foo*|*bar*
\"foo\"
"foo"
afooline
onebarvalue
now foo with spaces
_several_strings_

You can use the extglob option:

shopt -s extglob
string='@(foo|bar)'

You need two variables for case because the or | pipe is parsed before the patterns are expanded.

v1=foo v2=bar

case foo in ("$v1"|"$v2") echo foo; esac

foo

Shell patterns in variables are handled differently when quoted or unquoted as well:

q=?

case a in
("$q") echo question mark;;
($q)   echo not a question mark
esac

not a question mark