在Bash的case语句中使用正则表达式

62
我正在使用以下脚本,该脚本使用case语句来查找服务器。
    #!/bin/bash
SERVER=$1;
echo $SERVER | egrep "ws-[0-9]+\.host\.com";
case $SERVER in
ws-[0-9]+\.host\.com) echo "Web Server"
;;
db-[0-9]+\.host\.com) echo "DB server"
;;
bk-[0-9]+\.host\.com) echo "Backup server"
;;
*)echo "Unknown server"
;;
esac

但是它没有起作用。正则表达式在使用egrep时有效,但不适用于case。样例输出:

./test-back.sh ws-23.host.com
ws-23.host.com
Unknown server

有什么想法吗?

7个回答

65

Bash中的case语句并不使用正则表达式,而是仅使用shell模式匹配

因此,您应该使用模式匹配 ws*.host.com(或ws-+([0-9]).host.com,但这看起来有点高级,我从未尝试过 :-) ,而非正则表达式ws-[0-9]+\.host\.com


1
感谢@glenn_jackman,在某些机器上(CentOS 7.3,bash 4.2.46(1)),我遇到了语法错误"-bash: syntax error near unexpected token `('",针对模式+([a-zA-Z0-9])=*。在fc19的bash版本4.2.53(1)机器上没有语法错误 - extglob默认设置了。 - gaoithe
1
@gaoithe 谢谢您的评论!我在一个case语句中使用了+()结构,在交互模式下它可以工作,但在bash脚本中却被拒绝了。直到我看到了您的评论,我才明白了这一点。当我在我的脚本中开启了extglob选项后,问题就消失了。 - Onnonymous

18

仅供参考,不过已经在这个答案中提到了,在man bash的Pattern Matching一章节中提供了以下规则以形成复合模式:

可以使用以下一个或多个子模式来形成复合模式:

?(模式列表)
        匹配给定模式出现零次或一次。
*(模式列表)
        匹配给定模式出现零次或多次。
+(模式列表)
        匹配给定模式出现一次或多次。
@(模式列表)
        匹配给定模式之一。
!(模式列表)
        匹配任何模式之外的内容。

但是要使用这些扩展的模式匹配需要启用extglob shell选项。

这里是解决当前问题的示例代码:

shopt -s extglob;
SERVER="ws-45454.host.com";
case $SERVER in
        ws-+([0-9])\.host\.com) echo "Web Server"
                ;;
        db-+([0-9])\.host\.com) echo "DB server"
                ;;
        bk-+([0-9])\.host\.com) echo "Backup server"
                ;;
        *)echo "Unknown server"
                ;;
esac;
shopt -u extglob;

此外,可以使用以下命令:shopt | grep extglob 来检查其默认值。


1
你可以使用 shopt extglob 来查看它是否已设置,无需使用 grep - James Brown
您不必转义点号。 - 244an
哦,我多么希望它只是一个标准的正则表达式。 - Josh M.

18

如果你想断言*确实匹配ws*.host.com中的数字,并想使用case而不是ifelifelif,你可以使用类似以下代码:

case $SERVER in
  ws-[0123456789][0123456789][0123456789].host.com) echo "Web Server" ;;
  db-[0123456789][0123456789][0123456789].host.com) echo "DB server" ;;
  bk-[0123456789][0123456789][0123456789].host.com) echo "Backup server" ;;
  *) echo "Unknown server" ;;
esac

但是对于超过999个服务器,这种方法就不起作用了。

如果我必须为此编写一个脚本,我可能会写出类似以下的内容(因为我喜欢正则表达式和case语法 ;) ):

srv=`expr "$SERVER" : '^\(db\|bk\|ws\)-[0-9]\+\.host\.com$'`
echo -n "$SERVER : "
case $srv in
  ws) echo "Web Server" ;;
  db) echo "DB server" ;;
  bk) echo "Backup server" ;;
  *) echo "Unknown server !!!"
esac

5
case ... esac 的判断条件也可以写成 ws-[0-9][0-9][0-9].host.com) - Rockallite
1
你也可以使用[:digit:]代替。 - mashuptwice

13

case 只能使用通配符(globs),如果你想要进行正则表达式匹配,则需要使用一系列 if-then-else-elif-fi 语句,以及 [[


如何解决?[[ws-[0-9]+.host.com]])echo "Web服务器":无法工作。 - Unni
7
Ignacio 的意思是在级联的 if ... elif ... fi 语句中使用 [[ - glenn jackman

8

我知道这是一个比较老的问题,我的解决方案与@syjust提供的解决方案并没有太大区别,但我想展示在case/esac语句的匹配阶段你可以做任何事情。

$ cat case.sh && echo -e "#################\n" && bash case.sh ws-23.host.com
#!/bin/bash
SERVER=$1;
echo $SERVER | egrep "ws-[0-9]+\.host\.com";
case $SERVER in
  $(awk '{a=0}/ws-[0-9]*.host.com/{a=1}a' <<<${SERVER}))echo "Web Server";;
  $(awk '{a=0}/db-[0-9]*.host.com/{a=1}a' <<<${SERVER}))echo "DB Server";;
  $(awk '{a=0}/bk-[0-9]*.host.com/{a=1}a' <<<${SERVER}))echo "Backup Server";;
  *)echo "Unknown server";;
esac

#################

ws-23.host.com
Web Server


这是一个不错的想法,但为什么不直接使用 $(awk '/ws-[0-9]*.host.com/' <<< ${SERVER}) 呢?似乎没有必要使用 {a=0}{a=1}a - William Pursell
我很可能是从另一个我使用过的脚本中复制了它 - 现在想起来,我甚至不知道为什么我会在那个脚本中有它。我猜这是一种坏习惯。感谢您指出这一点。除了浪费33个按键之外,它并不会改变最终结果 - ! - AnthonyK

7
这里有一个使用 elif 结构的例子。
#!/bin/bash
SERVER=$1;
regex_ws="^ws-[0-9]+\.host\.com$"
regex_db="^db-[0-9]+\.host\.com$"
regex_bk="^bk-[0-9]+\.host\.com$"
if [[ "${SERVER}" =~ $regex_ws ]]; then
  echo "Web Server"
elif [[ "${SERVER}" =~ $regex_db ]]; then
  echo "DB server"
elif [[ "${SERVER}" =~ $regex_bk ]]; then
  echo "Backup server"
else
  echo "Unknown server"
fi

我发现将正则表达式存储在它们自己的变量中是最可靠的方法。


干净的解决方案,谢谢。 - snukone

5

您还可以使用expr进行匹配;它提供了类似grep的正则表达式语法,应该足够强大以满足此应用程序的需求。

#!/bin/bash

server=$1
if   expr "$server" : 'ws-[0-9]\+\.host\.com' >/dev/null; then echo "Web server"
elif expr "$server" : 'db-[0-9]\+\.host\.com' >/dev/null; then echo "DB server"
elif expr "$server" : 'bk-[0-9]\+\.host\.com' >/dev/null; then echo "Backup server"
else echo "Unknown server"
fi

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