如何使用变量作为 case 条件?

ter*_*don 22 bash shell-script case

我正在尝试使用由不同字符串组成的变量|作为case语句测试。例如:

string="\"foo\"|\"bar\""
read choice
case $choice in
    $string)
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac
Run Code Online (Sandbox Code Playgroud)

我希望能够输入foobar执行case语句的第一部分。然而,无论是foobar带我去第二:

$ foo.sh
foo
Bad choice!
$ foo.sh
bar
Bad choice!
Run Code Online (Sandbox Code Playgroud)

使用"$string"而不是$string没有区别。使用string="foo|bar".

我知道我可以这样做:

case $choice in
    "foo"|"bar")
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac
Run Code Online (Sandbox Code Playgroud)

我可以想到各种解决方法,但我想知道是否可以case在 bash 中使用变量作为条件。是否可能,如果可能,如何?

小智 25

bash 手册指出:

[[(] 模式 [ | 模式 ] ... ) 列表中的 case 词;; ] ... esac

检查的每个模式都使用波浪号扩展、参数和变量扩展、算术替换、命令替换和进程替换进行扩展。

没有«路径名扩展»

因此:模式不使用«路径名扩展»扩展。

因此:模式不能包含“|” 里面。仅:两个模式可以用“|”连接。

这有效:

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
Run Code Online (Sandbox Code Playgroud)

使用 « 扩展通配 »

但是,word匹配pattern使用« Pathname Expansion » 规则。
和 « Extended Globbing » here , here and, here允许使用交替 ("|") 模式。

这也有效:

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
Run Code Online (Sandbox Code Playgroud)

字符串内容

下一个测试脚本显示匹配所有包含任一foobar任何位置的行的模式是'*$(foo|bar)*'或 两个变量$s1=*foo*$s2=*bar*


测试脚本:

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_
Run Code Online (Sandbox Code Playgroud)


cho*_*oba 10

您可以使用以下extglob选项:

shopt -s extglob
string='@(foo|bar)'
Run Code Online (Sandbox Code Playgroud)

  • 啊没关系。`|` 不是 `foo|bar` 中模式的一部分,它是 `case` 语句语法的一部分,允许在一个子句中使用多个模式。不过,`|` *是*扩展模式的一部分。 (4认同)

mik*_*erv 5

您需要两个变量,case因为 or管道在模式扩展之前|被解析。

v1=foo v2=bar

case foo in ("$v1"|"$v2") echo foo; esac
Run Code Online (Sandbox Code Playgroud)
foo
Run Code Online (Sandbox Code Playgroud)

变量中的 Shell 模式在带引号或不带引号时的处理方式也不同:

foo
Run Code Online (Sandbox Code Playgroud)
not a question mark
Run Code Online (Sandbox Code Playgroud)