Bash中的不区分大小写比较

13

我试图在while语句中编写一个不区分大小写的比较。基本上,我只是想缩短以下内容,以便对用户的“是”或“否”问题提示进行操作...

while[ $yn == "y" | $yn == "Y" | $yn == "Yes" | $yn == "yes" ] ; do

如何最好地解决这个问题?


考虑使用正则表达式,可能会更短。 - Kent
可能是Shell脚本中字符串的大小写不敏感比较的重复问题。 - Riot
请注意,在开头[之前缺少一个空格。 - Benjamin W.
5个回答

15

不需要使用shopt或正则表达式。如果您有Bash 4,最简单、最快的方法是:

if [ "${var1,,}" = "${var2,,}" ]; then
  echo "matched"
fi

你所做的只是将两个字符串转换为小写并比较结果。


我刚刚尝试了一下,它起作用了。但我不确定是怎么做到的。你能提供文档链接或者告诉我这个结构的名称吗? - Angelo Fuchs
1
@AngeloFuchs ${my_variable,,} 语法是 Bash 参数扩展。请参阅 https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion 并滚动到该部分底部以获取大小写转换的信息。 - Riot

13
shopt -s nocasematch
while [[ $yn == y || $yn == "yes" ]] ; do
或:
shopt -s nocasematch
while [[ $yn =~ (y|yes) ]] ; do

注意

  • [[ 是类似于(但比)[ 命令更强大的bash关键字。请参见 http://mywiki.wooledge.org/BashFAQ/031http://mywiki.wooledge.org/BashGuide/TestsAndConditionals。 除非你是为 POSIX sh 编写,否则我们建议使用 [[
  • [[=~ 运算符会将左侧字符串与右侧扩展正则表达式(ERE)进行匹配评估。在成功匹配后,BASH_REMATCH 可用于扩展模式中匹配的组。引用了正则表达式的部分将变成文字。为了安全和兼容,请将正则表达式放入参数中,然后执行 [[ $string =~ $regex ]]

shopt -s nocasematch会使我的基于sed的查找和替换也不区分大小写吗? - user2150250
1
试一下,这只需要你15秒钟的时间 =) - Gilles Quénot
2
对于正则表达式,最好使用 ^..$,例如 yn="noyesno" - Kent
我其实想让所有字符都不区分大小写,但我认为只询问我指定的几个组合就足以从中推断出来了。您最初的while[[ $yn =~ (y|yes) ]]似乎完美地工作了。 - user2150250
或者 [[ $yn =~ y(es)? ]];顺便说一下,(y|yes) 中的括号并不是必需的。 - Benjamin W.

3

以下是使用扩展模式而不是正则表达式的答案:

shopt -s nocasematch
shopt -s extglob
while [[ $yn = y?(es) ]]; do
  ...
done

请注意,从4.1版本开始,bash[[ ... ]] 条件表达式内始终使用扩展模式,因此不需要添加 shopt 行。

我认为你的正则表达式中'?'的位置不对。这将匹配"yes"或"es",但不会匹配"y"。 - user1170868
1
这不是一个正则表达式,而是一个扩展模式。y匹配它本身;?(es)匹配模式es的零个或一个实例。 - chepner

1

我更喜欢使用grep -E或简称egrep

while egrep -iq '^(y|yes)$' <<< $yn; do
  your_logic_here
done

以下是来自 grep 手册的解释:

-i,--ignore-case

忽略模式和输入文件中的大小写区别。(-i由POSIX指定。)

-q,--quiet,--silent

安静模式;不向标准输出写入任何内容。即使检测到错误,如果找到任何匹配项,立即以零状态退出。也请参阅-s或--no-messages选项。(-q由POSIX指定。)


这样做是可行的,但是使用bash内部语法比调用外部程序更有效率。当然,在交互式脚本中差异不会有太大影响,因为人在循环中的速度比外部进程慢得多,但如果足够简单,我仍然更喜欢使用自己编写脚本语言的语法。 - joanis

1

try this one too:

 [[ $yn =~ "^[yY](es)?$" ]] 

如果我想让 "es" 也不区分大小写,我可以这样写 [[ $yn =~ "^[yY][eE][sS]" || $yn == [yY] ]]。我希望能够接受 "yEs"、"YeS" 和所有其他大小写组合。 - user2150250
@sputnick,请看一下问题和示例,只有 y 是不区分大小写的,es 部分只能小写... 这是我理解的。 - Kent
1
@user2150250,我建议你在进入while循环之前,先进行一次“tr”操作,将值全部转换为小写/大写。这样你就可以节省一些功夫。你只需要比较它是否等于“y”或“yes”(如果是小写的话)。 - Kent
如果您使用的是Bash v4.x+,则可以在不调用tr的情况下执行字符串大小写转换。 yn=${yn,,}; [[ $yn =~ ^yes ]] - Bruce
1
在这里引用右侧实际上会抑制正则表达式的解释(除了Bash 3.1及更早版本),因此对于非古老的Bash而言,这种方法不起作用。安全的方法是在单独的变量中声明正则表达式,然后在[[ ]]中使用它,不要加引号。 - Benjamin W.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接