case 语句中的 AND 运算符

smc*_*smc 6 shell bash shell-script case

我有以下代码。

read -p "Enter a word: " word

case $word in
  [aeiou]* | [AEIOU]*)
          echo "The word begins with a vowel." ;;
  [0-9]*)
          echo "The word begins with a digit." ;;
  *[0-9])
          echo "The word ends with a digit." ;;
  [aeiou]* && [AEIOU]* && *[0-9])
          echo "The word begins with vowel and ends with a digit." ;;
   ????)
          echo "You entered a four letter word." ;;
   *)
          echo "I don't know what you've entered," ;;
esac
Run Code Online (Sandbox Code Playgroud)

当我运行这个:

Enter a word: apple123
case2.sh: line 10: syntax error near unexpected token `&&'
case2.sh: line 10: `  [aeiou]* && [AEIOU]* && *[0-9])'
Run Code Online (Sandbox Code Playgroud)

看起来 case 语句不支持 AND 运算符,而且我认为上面 case 语句中的 && 运算符在逻辑上是不正确的。

我知道我们可以使用 if else 来检查输入是否以元音和数字开头。但我很好奇 case 是否有任何内置函数,如 AND 运算符。

Jef*_*ler 11

您是正确的,因为标准定义case不允许在模式中使用 AND 运算符。你也正确,试图说“以小写元音开头并以大写元音开头”不会匹配任何内容。还要注意的是你有你的模式和反向解释为以数字开头的测试/两端-使用的模式[0-9]*会匹配单词开始以数字,而不是结束以数字。

一种方法是将您的测试组合成相同的模式,首先是最严格的:

case $word in
  ([AaEeIiOoUu]??[0-9]) echo it is four characters long and begins with a vowel and ends with a digit;;
  ([AaEeIiOoUu]*[0-9])  echo it is not four characters long begins with a vowel and ends with a digit;;
# ...
esac
Run Code Online (Sandbox Code Playgroud)

另一种(冗长的!)方法是嵌套你的case陈述,每次都建立适当的回应。它是否以元音开头,是或否?现在,它是否以数字结尾,是或否?这会很快变得笨拙,并且维护起来很烦人。

另一种方法是使用一系列case语句来构建适用语句的字符串(或数组);你甚至可以添加*包罗万象的模式,以每个如果你想提供“负”反馈(“二字确实不是以元音开头”,等等)。

result=""
case $word in
  [AaEeIiOoUu]*)
          result="The word begins with a vowel." ;;
esac

case $word in
  [0-9]*)
          result="${result} The word begins with a digit." ;;
esac

case $word in
  *[0-9])
          result="${result} The word ends with a digit." ;;
esac

case $word in
   ????)
          result="${result} You entered four characters." ;;
esac

printf '%s\n' "$result"
Run Code Online (Sandbox Code Playgroud)

举些例子:

$ ./go.sh
Enter a word: aieee
The word begins with a vowel.
$ ./go.sh
Enter a word: jeff42
 The word ends with a digit.
$ ./go.sh
Enter a word: aiee
The word begins with a vowel. You entered four characters.
$ ./go.sh
Enter a word: 9arm
 The word begins with a digit. You entered four characters.
$ ./go.sh
Enter a word: arm9
The word begins with a vowel. The word ends with a digit. You entered four characters.
Run Code Online (Sandbox Code Playgroud)

或者,bash 扩展了case语句的语法以允许选择多个模式,如果您以以下方式结束模式;;&

shopt -s nocasematch
case $word in
  [aeiou]*)
          echo "The word begins with a vowel." ;;&
  [0-9]*)
          echo "The word begins with a digit." ;;&
  *[0-9])
          echo "The word ends with a digit." ;;&
   ????)
          echo "You entered four characters." ;;
esac
Run Code Online (Sandbox Code Playgroud)

请注意,我删除了*全能模式,因为当以这种方式通过模式时,它会匹配任何内容。Bash 还有一个名为 的 shell 选项nocasematch,我在上面设置了它,它可以对模式进行不区分大小写的匹配。这有助于减少冗余——我删除| [AEIOU]*了模式的一部分。

举些例子:

$ ./go.sh
Enter a word: aieee
The word begins with a vowel.
$ ./go.sh
Enter a word: jeff42
The word ends with a digit.
$ ./go.sh
Enter a word: aiee
The word begins with a vowel.
You entered four characters.
$ ./go.sh
Enter a word: 9arm
The word begins with a digit.
You entered four characters.
$ ./go.sh
Enter a word: arm9
The word begins with a vowel.
The word ends with a digit.
You entered four characters.
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 6

为了完整起见,虽然case|OR 运算符,但它没有 AND 运算符,但如果使用带有扩展 glob 运算符(ksh、zsh、bash)的 shell,您可以在模式语法中实现 AND :

  • ksh93@(x&y&z)运营商:

      case $string in
        ( @({12}(?)&~(i:[aeiou]*)&*[0123456789]) )
          echo is 12 characters long AND starts with a vowel AND ends in a decimal
      esac
    
    Run Code Online (Sandbox Code Playgroud)
  • zsh(使用~(AND-NOT)结合^(NOT)):x~^y~^z

      set -o extendedglob
      case $string in
        ( ?(#c12)~^(#i)[aeiou]*~^*[0-9] )
          echo is 12 characters long AND starts with a vowel AND ends in a decimal
      esac
    
    Run Code Online (Sandbox Code Playgroud)
  • ksh88, bash, 使用带 OR ( !(!(x)|!(y)|!(z))) 的双重否定

      shopt -s extglob # bash only
      case $string in
        ( !(!(????????????)|!([aAeE?iI?oOuU]*)|!(*[0123456789])) )
          echo is 12 characters long AND starts with a vowel AND ends in a decimal
      esac
    
    Run Code Online (Sandbox Code Playgroud)

在任何情况下,请记住,除了在 zsh 中范围始终基于代码点值[0-9]之外,在 POSIX/C 语言环境之外不能可靠地使用类似的范围(因此是[0123456789]上面的替代)。

ksh93 和 zsh 的不区分大小写匹配运算符(~(i)(#i))尊重区分大小写比较的语言环境。例如,在GNU系统上的土耳其语言环境,(#i)[aeiou]将匹配上?,而不是I(因为大写i?那里)。为了在不考虑语言环境的情况下获得一致的结果,您可能需要像 ksh88/bash 方法那样对所有可能的值进行硬编码。