企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
总第5篇 之前,我们谈到了if。这次我们来谈另一种选择结构——case。 ## case与if if用于选择的条件,不是很多的情况,如果选择的条件太多,一系列的if,elif,,也是醉了。没错,case在bash中的地位,和switch在C中的地位一样。但是用法可大有不同。 ### 代码块 任何语言都有代码块的概念,C语言中的花括号{ },python中的冒号和缩进。bash中的代码块风格不是很统一。但是在选择结构中是相同的——**反语**。if结构使用***if...fi***标识一个代码块的作用域,而case也是用***case...esac***表示块作用域的。。 ## 基本结构 看例子echo.sh: ~~~ #!/bin/bash str="is good " read -p "你喜欢什么颜色:" color case $color in red) echo -e "\e[1;31m$color $str \e[0m";; green) echo -e "\e[1;32m$color $str \e[0m";; yellow) echo -e "\e[1;33m$color $str \e[0m";; blue) echo -e "\e[1;34m$color $str \e[0m";; *) echo -e "\e[1;30m这是什么颜色?\e[0m";; esac ~~~ 这段代码,就是根据你的输入来显示不同颜色的文本。关于echo语句的写法,这里不细说,每句分别设置了不同的文本颜色。重点去看一下这个结构。 把变量color的值,依次和下面右括号里的值做比较,如果相同,就执行后面的语句。 来运行一下 ![](https://box.kancloud.cn/2016-02-19_56c6cdb3241a2.jpg) ### 两个分号;; 注意每个条件末尾都是两个分号。这很好理解,就像C语言的switch里面每个case都经常会有一个break一样。因为每个条件满足的时候,需要执行的语句可能不止一句。而在bash中多个语句在一行是用一个分号间隔的。这样两个分号就表示着语句结束,另外加一个空语句,也就是说要执行的语句都完成了。 与c语言的switch不同的是,c语言中每个条件(case语句)后面不一定都要有break,如果没有break,则继续执行下面的条件的语句。如果你想像c语言那样多个条件共用一套语句,怎么办呢?请继续阅读。 ### 分号扭号;& 所谓扭号,就是And符号——&,我觉得叫扭号更简洁。。看一段C代码: ~~~ #include<stdio.h> int main(){ char ans; printf("你喜欢编程么:"); scanf("%c",&ans); switch(ans){ case 'y': case 'Y':printf("我也是\n");break; case 'n': case 'N':printf("sorry,跟你没什么好谈的\n");break; } } ~~~ 也就是说,在C中,case后面如果不加break,那么在执行完匹配的语句后,将接着执行,而不管下面的条件匹配不匹配,直到遇到break为止。这样的语法,十分灵活,那么bash中又如何实现呢?难道在末尾加一个分号?错错错。答案是一个分号加一个扭号。 ~~~ #!/bin/bash read -p "你喜欢编程么:(y/n):" ans case $ans in y);& Y) echo "我也是";; n);& N) echo "sorry,跟你没什么好谈的";; esac ~~~ ### 分号分号扭号;;& 除了 ;&结束语句以外,还有一种是 ;;& 来结束语句的用法。但是意义有所不同,;;&的用法是使得条件越来越**精确**。 ~~~ #!/bin/bash read -p "请输入一个区号:" num case $num in *)echo -n "中国";;& 03*)echo -n "河北省";;& ??10)echo "邯郸市";; ??11)echo "石家庄";; ??17)echo "沧州市";; 07*)echo -n "江西省";;& ??91)echo "南昌市";; ??92)echo "九江市";; ??97)echo "赣州市";; esac ~~~ 关于通配符*和?,我们下面会讲到。这里,你只需要知道,条件语句以 ;;& 结束的时候,程序执行到这里不会停止,会继续测试下面条件,如果满足继续执行,直到遇到 ;; 或esac ![](https://box.kancloud.cn/2016-02-19_56c6cdb33b136.jpg) *注意,以上这两种扭号的用法,是bash自己的 特性,对于其他shell,并不支持。如果考虑移植性,就不要这样写了。仅仅用正规的两个分号就可以了。* ## 右括号中的模式 ### 基本正则 )右括号,类似c语言中switch中的case。然而与之不同的是,Bash中的右括号里面,不仅支持完整的字符串(c语言中只支持整型数据,包括int和char ,不支持字符串)还支持“模式”匹配。模式的概念,如果了解正则表达式,那么就很容易理解了。不过case语句中与正则表达式略有不同。貌似支持的并不完整。 - *是匹配0个或多个任何字符。 - ?是匹配一个字符。可以看做一个占位符。 - [ ]表示一个范围。 - ( )枚举字符串。但是需要转义 ~~~ #!/bin/bash read -p "请输入一个数字:" num case $num in 2*)echo "匹配2*";;& 2?)echo "匹配2?";;& [0-9])echo "匹配[0-9]";; esac ~~~ 方括号表示范围,你可以使用[123]来匹配1或2或3,而不是123。也可以使用[0-9]、[a-zA-Z]这样的写法,熟悉正则的同学,这些都不是问题。 注意我上面用到了 ;;& 也就是说在匹配了一个模式之后,不会停止,还会继续向下执行。执行效果: ![](https://box.kancloud.cn/2016-02-19_56c6cdb351837.jpg) ###枚举字符串 一般的写法比如(123|456|789)匹配123,456,789这三个数。但是因为case中右括号有特殊含义,所以进行了转义。 然后我的写法就是(123|456|789\),但是这样有个问题就是789无法得到匹配,123和456倒是可以,我也是不明所以(见笑,知情者望告知)。 于是我又改了一版,终于匹配完全——(123|456|789|\)。 现在加上一句到刚才的脚本中——(123|456|789 \))echo "匹配(123|456|789|\)";;& ![](https://box.kancloud.cn/2016-02-19_56c6cdb368aa2.jpg) ### POSIX字符类 一般的编程语言中的正则表达式,支持\w,\d这类的字符类(Character Classes)。而Unix-like系统上,支持的字符类是POSIX风格的。 见下表 <table border="1" cellpadding="0" cellspacing="0" width="495"><tbody><tr><td><p>类</p></td><td><p>描  述</p></td><td><p>扩  展</p></td></tr><tr><td><p><tt>[:alnum:]</tt></p></td><td><p>字母和数字字符</p></td><td><p><tt>[0-9a-zA-Z]</tt></p></td></tr><tr><td><p><tt>[:alpha:]</tt></p></td><td><p> (letters)字母字符(字母)</p></td><td><p><tt>[a-zA-Z]</tt></p></td></tr><tr><td><p><tt>[:ascii:]</tt></p></td><td><p>7位ASCII</p></td><td><p><tt>[\x01-\x7F]</tt></p></td></tr><tr><td><p><tt>[:blank:]</tt></p></td><td><p>水平空白符(空格、制表符)</p></td><td><p><tt>[ \t]</tt></p></td></tr><tr><td><p><tt>[:cntrl:]</tt></p></td><td><p>控制字符</p></td><td><p><tt>[\x01-\x1F]</tt></p></td></tr><tr><td><p><tt>[:digit:]</tt></p></td><td><p>数字</p></td><td><p><tt>[0-9]</tt></p></td></tr><tr><td><p><tt>[:graph:]</tt></p></td><td><p>用墨水打印的字符(非空格、非控制字符)</p></td><td><p><tt>[^\x01-\x20]</tt></p></td></tr><tr><td><p><tt>[:lower:]</tt></p></td><td><p>小写字母</p></td><td><p><tt>[a-z]</tt></p></td></tr><tr><td><p><tt>[:print:]</tt></p></td><td><p>可打印字符(图形类加空格和制表符)</p></td><td><p><tt>[\t\x20-\xFF]</tt></p></td></tr><tr><td><p><tt>[:punct:]</tt></p></td><td><p>任意标点符号,如句点(.)和分号(;)</p></td><td><p><tt>[-!"#$%&amp;'( )*+,./:;&lt;=&gt;?@[\\\]^_'{|}~]</tt></p></td></tr><tr><td><p><tt>[:space:]</tt></p></td><td><p>空白(换行、回车、制表符、空格、垂直制表符)</p></td><td><p><tt>[\n\r\t \x0B]</tt></p></td></tr><tr><td><p><tt>[:upper:]</tt></p></td><td><p>大写字母</p></td><td><p><tt>[A-Z]</tt></p></td></tr><tr><td><p><tt>[:xdigit:]</tt></p></td><td><p>十六进制数字</p></td><td><p><tt>[0-9a-fA-F]</tt></p></td></tr></tbody></table> 这个表是从网友那里复制的,原文[《POSIX风格正则表达式](http://blog.csdn.net/joeblackzqq/article/details/7216046)》 实际使用的时候,还要在这些字符类的方括号外面,再套一层方括号才行。 ~~~ #!/bin/bash read -p "请任意输入,可以包含空格等空白符" ch #echo $ch case $ch in *[[:lower:]]*)echo lower;;& *[[:upper:]]*)echo upper;;& *[[:digit:]]*)echo digit;;& *[[:punct:]]*)echo punct;;& *[[:ascii:]]*)echo ascii;;& *[[:cntrl:]]*)echo cntrl;;& *[[:print:]]*)echo print;;& *[[:space:]]*)echo space;;& *[[:xdigit:]]*)echo xdigit;;& *[[:graph:]]*)echo graph;;& *[[:blank:]]*)echo blank;;& *[[:alnum:]]*)echo alnum;;& *[[:alpha:]]*)echo alpha;;& esac ~~~ 这个脚本,大家自己去测试吧。当然了有些字符(比如控制字符)貌似不好输入。这样不好测试[:cntrl:]这个字符类,这里我告诉您一下,ascii码中27对应的控制字符就是ESC。测试脚本的时候,可以按一下ESC键。或者键入**^[**字符,也是一样的。 本系列(玩转Bash脚本)更多文章,请访问:[http://blog.csdn.net/column/details/wanbash.html](http://blog.csdn.net/column/details/wanbash.html)[](http://blog.csdn.net/column/details/wanbash.html)