我有以下代码。
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
当我运行这个时:
Enter a word: apple123
case2.sh: line 10: syntax error near unexpected token `&&'
case2.sh: line 10: ` [aeiou]* && [AEIOU]* && *[0-9])'
看起来 case 语句不支持 AND 运算符,而且我相信上面 case 语句中的 && 运算符在逻辑上不正确。
我知道我们可以使用 if else 来检查输入是否以元音和数字开头。但我很好奇 case 是否有任何内置函数,如 AND 运算符。
答案1
你是对的标准定义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
另一种(冗长!)方法是嵌套您的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"
举些例子:
$ ./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.
或者,case
bash 扩展了语句的语法允许选择多个模式,如果您以以下方式结束模式;;&
:
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
请注意,我删除了*
包罗万象的模式,因为当以这种方式穿过模式时,它会匹配任何内容。 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.
答案2
为了完整起见,虽然case
有|
OR 运算符,但它没有 AND 运算符,但如果使用带有扩展 glob 运算符(ksh、zsh、bash)的 shell,则可以在图案句法:
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
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
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
在任何情况下,请记住,除了 zsh 中的范围始终基于代码点值之外,类似的范围[0-9]
不能在 POSIX/C 语言环境之外可靠地使用(因此是[0123456789]
上面的替代方案)。
ksh93 和 zsh 的不区分大小写的匹配运算符 (~(i)
和(#i)
) 遵循区域设置以进行区分大小写的比较。例如,在 GNU 系统的土耳其语言环境中,(#i)[aeiou]
将匹配İ
,但不会匹配I
(因为存在大写字母i
)İ
。为了获得一致的结果(无论语言环境如何),您可能需要对所有可能的值进行硬编码,而不是像 ksh88/bash 方法中那样。
答案3
通常便携的在 case 语句中实现 AND 的解决方案是连接布尔值:
case $A$B in
11) echo "Both conditions are true";;
1*) echo "Condition A is true";;
*1) echo "Condition B is true";;
00) echo "Both conditions are false";;
*) echo "There is an unexpected error";;
esac
对于您的用例:
printf "Enter a word: "; read word
A=0 B=0 C=0
case $word in ( [aeiouAEIOU]* ) A=1;; esac
case $word in ( *[0-9] ) B=1;; esac
case $word in ( ???? ) C=1;; esac
case $A$B$C in
111) echo "Four letters that start with a vowel and end with a digit" ;;
11*) echo "The word begins with a vowel AND ends with a digit." ;;
1* ) echo "The word begins with a vowel." ;;
*1?) echo "The word ends with a digit." ;;
*1) echo "The word is four letters long" ;;
*) echo "I don't understand what you've entered," ;;
esac
每个布尔选项都使用便携式外壳。您可以;;&
在 bash 或zsh 中使用;|
。遗憾的是 ksh 没有这样的选项。
设置布尔值的另一种方法(至少在某些 shell 中:ksh、bash、zsh)是:
[[ $word == [aeiouAEIOU]* ]] && A=1 || A=0
[[ $word == *[0-9] ]] && B=1 || B=0
[[ $word == ???? ]] && C=1 || c=0