如何使用变量作为大小写条件?


17

我试图使用由具有分离的不同的字符串变量|作为case语句测试。例如:

string="\"foo\"|\"bar\""
read choice
case $choice in
    $string)
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

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

$ foo.sh
foo
Bad choice!
$ foo.sh
bar
Bad choice!

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

我知道我可以这样:

case $choice in
    "foo"|"bar")
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

我可以想到各种解决方法,但我想知道是否有可能case在bash中将变量用作条件。有可能吗?


2
我无法提出一个真正的答案,但由于没有人提到它,因此您可以将case语句包装在eval,转义的$ choice,括号,星号,分号和换行符中。丑陋,但“有效”。
杰夫·谢勒

@JeffSchaller-很多时候这不是一个坏主意,在这种情况下可能只是门票。我也考虑过推荐它,但是read有点阻止了我。在我看来,用户输入验证,这是什么,这似乎是,case模式应该不会是在评估列表的顶部,而应修剪下来的*默认模式,使得到达那里的唯一结果是保证接受的。但是,由于问题是解析/扩展顺序,因此可能需要第二次评估。
mikeserv

Answers:


13

bash手册指出:

[[(]模式[|模式] ...)列表中的大小写单词;] ... 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

使用«扩展globbing»

但是,wordpattern使用“路径名扩展”规则匹配。此处此处此处的
“扩展globbing” 允许使用交替的(“ |”)模式。

这也可以:

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

字符串内容

下一个测试脚本显示匹配所有包含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_

9

您可以使用以下extglob选项:

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

有趣; 与什么@(foo|bar)相比有什么特别之处foo|bar?两者都是有效的模式,在按字面值键入时,它们的工作原理相同。
chepner 2015年

4
啊没关系。|不是的模式foo|bar的一部分,而是case语句的语法的一部分,以允许在一个子句中使用多个模式。| 扩展模式的一部分。
chepner 2015年

3

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

v1=foo v2=bar

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

foo

引用或不引用时,变量中的外壳模式的处理方式也不同:

q=?

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

not a question mark

-1

如果您想要一个破折号兼容的解决方法,您可以编写:

  string="foo|bar"
  read choice
  awk 'BEGIN{
   n=split("'$string'",p,"|");
   for(i=1;i<=n;i++)
    if(system("\
      case \"'$choice'\" in "p[i]")\
       echo \"You chose '$choice'\";\
       exit 1;;\
      esac"))
     exit;
   print "Bad choice"
  }'

说明:awk用于拆分“字符串”并分别测试每个部分。如果“ choice”与当前测试的部分p [i]相匹配,则awk命令将在第11行中以“ exit”结束。对于该测试,将使用shell的“ case”(在“ system”调用中) ,按照terdon的要求。这样就保留了将预期的test-“ string”修改为“ foo * | bar”的可能性,以便也可以与shell的“ case”允许的“ foooo”(“搜索模式”)匹配。相反,如果您更喜欢正则表达式,则可以省略“ system”调用,而使用awk的“〜”或“ match”。


亲爱的唐纳德·沃特,如果您能告诉我您的唐纳德·杜威的原因,我最好能学会如何避免使用无用的解决方案。
杰拉尔德·谢德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.