bash / bourne中有“ in”运算符吗?


17

我正在寻找一个类似这样的“ in”运算符:

if [ "$1" in ("cat","dog","mouse") ]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

与使用多个“或”测试相比,这显然是一条短得多的语句。

Answers:


31

您可以使用case...esac

$ cat in.sh 
#!/bin/bash

case "$1" in 
  "cat"|"dog"|"mouse")
    echo "dollar 1 is either a cat or a dog or a mouse"
  ;;
  *)
    echo "none of the above"
  ;;
esac

例如

$ ./in.sh dog
dollar 1 is either a cat or a dog or a mouse
$ ./in.sh hamster
none of the above

随着kshbash -O extglob或者zsh -o kshglob,你也可以使用扩展的glob模式:

if [[ "$1" = @(cat|dog|mouse) ]]; then
  echo "dollar 1 is either a cat or a dog or a mouse"
else
  echo "none of the above"
fi

随着bashksh93或者zsh,你也可以使用正则表达式比较:

if [[ "$1" =~ ^(cat|dog|mouse)$ ]]; then
  echo "dollar 1 is either a cat or a dog or a mouse"
else
  echo "none of the above"
fi

有什么理由使用双括号?再次感谢!
mrjayviper

1
@mrjayviper双括号内是一个扩展的测试构建体- AFAIK正则表达式运算符=~不是POSIX单托架测试内有效
steeldriver

1
@steeldriver Only仅[是POSIX,但是[[是bash,ksh的扩展功能(显然它起源于此,而zsh。 case示例是所有POSIX中的大多数)
Sergiy Kolodyazhnyy

2
@StéphaneChazelas由于至少使用bash 4.1-alpha,因此无需显式设置extglog。从bash的变化秒。为兼容性而将==和!=运算符的模式参数解析为[[命令时],请临时强制extglob。
艾萨克(Isaac)

@steeldriver $ 1 in case "$1" in不需要用引号引起来,在该令牌中不执行词拆分或路径名扩展。
艾萨克(Isaac)

9

bash中没有“ in”测试,但是有一个正则表达式测试(bourne中没有):

if [[ $1 =~ ^(cat|dog|mouse)$ ]]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

并且通常使用变量编写(较少的引用问题):

regex='^(cat|dog|mouse)$'

if [[ $1 =~ $regex ]]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

对于较旧的Bourne外壳,您需要使用大小写匹配:

case $1 in
    cat|dog|mouse)   echo "dollar 1 is either a cat or a dog or a mouse";;
esac

这是我接受的答案。
Nam G VU

6

case如果您有固定的宠物要配对,使用a 会很好。但是,如果您需要在运行时中构建模式,它将无法正常工作,因为case无法从扩展参数中解释替代。

这将仅匹配文字字符串cat|dog|mouse

patt='cat|dog|mouse'
case $1 in 
        $patt) echo "$1 matches the case" ;; 
esac

但是,您可以将变量与正则表达式匹配使用。只要不引用该变量,该变量中的任何正则表达式运算符都有其特殊含义。

patt='cat|dog|mouse'
if [[ "$1" =~ ^($patt)$ ]]; then
        echo "$1 matches the pattern"
fi

您也可以使用关联数组。检查密钥是否存在与inBash提供的运算符最接近。虽然语法有点丑陋:

declare -A arr
arr[cat]=1
arr[dog]=1
arr[mouse]=1

if [ "${arr[$1]+x}" ]; then
        echo "$1 is in the array"
fi

${arr[$1]+x}扩展为xif arr[$1]设置,否则为空。


5

可以caseif测试中使用一条语句,但是代码看起来有些毛茸茸:

if case "$1" in (cat|dog|mouse) true ;; (*) false; esac; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

或稍短,

if ! case "$1" in (cat|dog|mouse) false; esac; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

这是使用case子句只是为了对该if子句进行模式匹配。它引入了不必要的对/错测试。

最好只使用case

case "$1" in
    cat|dog|mouse)
        printf '"%s" is one of cat, dog or mouse\n' "$1"
        ;;
    *)
        printf '"%s" is unknown\n' "$1"
esac

不要这样做:

is_one_of () {
    eval "case $1 in ($2) return 0; esac"
    return 1
}

if is_one_of "$1" 'cat|dog|mouse'; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

或这个:

is_one_of () (
    word=$1
    shift
    IFS='|'
    eval "case $word in ($*) return 0; esac"
    return 1
)

if is_one_of "$1" cat dog mouse; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

...因为您只是添加了更多危险的东西,只是为了能够if在代码中使用一个语句来代替一个完全合理的case语句。


将案例分为函数调用并在if语句中评估退出状态是否更好?
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy并将模式作为函数的参数吗?它必须做一个eval对的case声明在这种情况下,它会更容易出错。
库沙兰南达

在这种情况下,有一个简单的模式,每个模式都可以作为位置参数传递给function和do "$1"|"$2"|"$3"。也是unix.stackexchange.com/a/234415/85039 但是,是的,这有点毛。
Sergiy Kolodyazhnyy

4

grep 方法。

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -q避免任何输出到屏幕(键入要比快>/dev/null)。
  • -E对于扩展的正则表达式,(cat|dog|mouse)方面需要这样做。
  • ^(cat|dog|mouse)$与以(^)以猫,狗或老鼠((cat|dog|mouse))开头的任何行,然后以($)行的末尾匹配
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.