Bash脚本编程学习笔记06:条件结构体
简介
在bash脚本编程中,条件结构体使用if语句和case语句两种句式。
if语句
单分支if语句
if test; then cmd fi
test:条件判断,多数情况下可使用test命令来实现,返回值为0的话则执行cmd,否则就离开该条件结构体,脚本继续往下执行。
[root@c7-server ~]# cat test.sh #!/bin/bash if id zwl &> /dev/null; then echo "user zwl exists." fi [root@c7-server ~]# bash test.sh user zwl exists.
双分支if语句
if test; then
cmd-true else
cmd-false fi
为真执行cmd-true,为假执行cmd-false。
[root@c7-server ~]# cat test.sh #!/bin/bash read -p "pleas input a user name:" name if id $name &> /dev/null; then echo "user $name exists." else echo "user $name doesn't exists." fi [root@c7-server ~]# bash test.sh pleas input a user name:zwl user zwl exists. [root@c7-server ~]# bash test.sh pleas input a user name:alongdidi user alongdidi doesn't exists.
多分支if语句
if test1; then cmd1 elif test2; then cmd2 elif test3; then cmd3 ... else cmd-last fi
当test1为真时执行cmd1,否则判断test2;当test2为真时执行cmd2,否则判断test3;以此类推,都不符合条件的话则执行cmd-last。
判断文件类型的示例。
#!/bin/bash read -p "please input only a absolute file path:" file if [ -z $file ]; then echo "you must input something." exit 2 fi if [ ! -e $file ]; then echo "no such file $file" elif [ -d $file ]; then echo "file $file is a directory." elif [ -l $file ]; then echo "file $file is a symbolic." elif [ -b $file ]; then echo "file $file is a block special file." elif [ -c $file ]; then echo "file $file is a character special file." elif [ -s $file ]; then echo "file $file is a socket file." elif [ -f $file ]; then echo "file $file is a regular file." else echo "file is unrecognized." fi
执行示例。
[root@c7-server ~]# bash test.sh please input only a absolute file path: you must input something. [root@c7-server ~]# bash test.sh please input only a absolute file path:passwd no such file passwd [root@c7-server ~]# bash test.sh please input only a absolute file path:/etc/passwd file /etc/passwd is a regular file [root@c7-server ~]# bash test.sh please input only a absolute file path:/root/ file /root/ is a directory. [root@c7-server ~]# bash test.sh please input only a absolute file path:/etc/rc.local file /etc/rc.local is a symbolic.
字符链接文件也可以被认为是普通文件(regular),因此建议将普通文件的判定放置在较靠后的位置。
注意:if语句是可以嵌套的。
[root@c7-server ~]# cat test.sh #!/bin/bash if [ -e /dev/sda ]; then if [ -b /dev/sda ]; then echo "it's a block file." fi fi [root@c7-server ~]# bash test.sh it's a block file.
其他示例
编写一个脚本,仅可接收一个参数,此参数应是一个用户名称。判断该用户名是否存在,若存在则输出用户的信息,否则就创建该用户并设置默认密码(password)。
#!/bin/bash if [ $# -ne 1 ];then echo "you must input just one argument!" exit 2 fi if id $1 &> /dev/null; then id $1 else useradd $1 echo "password" | passwd --stdin $1 &> /dev/null fi
编写一个脚本,接收两个数值类参数,并输出其中较大的那个。
#!/bin/bash if [ $# -ne 2 ]; then echo "please input exact two number arguments." exit 1 fi if [ $1 -eq $2 ]; then echo "number $1 and $2 are equal." elif [ $1 -gt $2 ]; then echo "the greater is $1." else echo "the greater is $2." fi
编写一个脚本,接收一个用户名作为参数,并判断其奇偶性。
[root@c7-server ~]# cat even_odd_if.sh #!/bin/bash if [ $# -ne 1 ]; then echo "you must input just one argument." exit 1 fi var=$[$(id -u $1)%2] if [ $var -eq 0 ]; then echo "the uid of $1 is even." else echo "the uid of $1 is odd." fi
编写一个脚本,接收两个文件名作为参数,返回文件的行数以及判断哪个文件的行数比较多。
#!/bin/bash if [ $# -ne 2 ]; then echo "you must input exat 2 arguments." exit 1 fi if [ ! -e $1 -o ! -e $2 ]; then echo "file $1 or/and $2 doesn't/don't exist[s]." exit 2 fi line1=$(wc -l $1 | cut -d " " -f 1) line2=$(wc -l $2 | cut -d " " -f 1) echo "the lines of $1 is $line1" echo "the lines of $2 is $line2" if [ $line1 -gt $line2 ]; then echo "$1 has more lines." elif [ $line1 -lt $line2 ]; then echo "$2 has more lines." else echo "they have same lines." fi
编写一个脚本,传递一个用户名作为参数给脚本,判断用户的类型。
uid=0:管理员
uid=1~999:系统用户
uid=1000+:普通用户
#!/bin/bash if [ $# -ne 1 ]; then echo "you must input exact one argument." exit 1 fi if ! id $1 &> /dev/null; then echo "you must input an existed username." exit 2 fi userid=$(id -u $1) if [ $userid -eq 0 ]; then echo "$1 is a admin user." elif [ $userid -lt 1000 ]; then echo "$1 is a system user." else echo "$1 is a normal user." fi
编写一个脚本,展示一个菜单供用户选择,菜单告知用户脚本可以显示的系统信息。
#!/bin/bash cat << eof disk) show disk infomation. mem) show memory infomation. cpu) show cpu infomation. *) quit! eof read -p "your option is: " option if [ -z $option ]; then echo "you input nothing,quit!" exit 1 elif [ $option == disk ]; then fdisk -l elif [ $option == mem ]; then free -m elif [ $option == cpu ]; then lscpu else echo "you input a illegal string,quit now!" fi
在后面学习了循环之后,可以加上循环,使得用户在输入错误的情况下,反复让用户输入直到输入正确的选项。
#!/bin/bash cat << eof disk) show disk infomation. mem) show memory infomation. cpu) show cpu infomation. *) again! eof read -p "your option is: " option while [ "$option" != disk -a "$option" != mem -a "$option" != cpu -o "$option" == "" ]; do echo "you input a illegal string. usage {disk|mem|cpu}, case sensitive." read -p "your option is: " option done if [ $option == disk ]; then fdisk -l elif [ $option == mem ]; then free -m elif [ $option == cpu ]; then lscpu fi
这个脚本的难点我觉得在于while循环中的判断应该怎么写,$option是否应该加引号、字符串匹配右边的字符(如disk)是否需要加引号、使用单中括号还是双中括号、使用单引号还是双引号。我也是一遍遍试直到瞎猫碰到死耗子才写出来符合自己需求的bash代码。
具体涉及的难点包括但《bash脚本编程学习笔记04:测试命令test、状态返回值、位置参数和特殊变量》文章开头说的那些,因此这里无法为大家做到准确的分析。
case语句
像上述脚本中,我们反复对一个变量做字符串等值比较并使用了多分支的if语句。此类情况我们完全可以使用case语句来代替,使其更容易看懂。
其官方语法如下:
case word in [ [(] pattern [| pattern]…) command-list ;;]… esac
case会将word和pattern进行匹配,一旦匹配到就执行对应的command-list,并且退出。
pattern基于bash的模式匹配,即glob风格。
pattern至少一个,可以有多个使用“|”分隔。
pattern+command-list成为一个子句(clause),如下。
[(] pattern [| pattern]…) command-list ;;
每个子句,都会以“;;”或者“;&”或者“;;&”结束。基本上只会使用“;;”。
;;:决定了一旦word第一次匹配到了pattern,就执行对应的command-list,并且退出。 ;&和;;&:而这两个是不会在第一次匹配到就立刻退出的,还会有其他后续的动作,几乎很少用到,有需要的可以去看手册。
word在匹配前会经历:波浪符展开、参数展开、命令替换、算术展开和引号去除。
pattern会经历:波浪符展开、参数展开、命令替换和算术展开。
当word的值是一个通配符的时候,表示默认的case。类似多分支if语句最后的else。
来个官方示例,简单易懂。
#!/bin/bash echo -n "enter the name of an animal: " read animal echo -n "the $animal has " case $animal in horse | dog | cat) echo -n "four";; man | kangaroo ) echo -n "two";; *) echo -n "an unknown number of";; esac echo " legs."
学会了case语句后,我们就可以对上面的多分支if语句的最后一个示例(显示系统信息的)进行改写,改为case语句的。应该不难,这里不演示了,我们尝试新的脚本。
我们尝试写一个bash服务类脚本,常见于centos 6系列的系统中的/etc/rc.d/init.d/目录下。
- 脚本的名称一般就是服务名称,不会带“.sh”。
- 服务脚本一般会创建一个空文件作为锁文件,若此文件存在则表示服务处于运行状态;反之,则服务处于停止状态。
- 脚本只能接收四种参数:start, stop, restart, status。
- 我们并不会真正启动某进程,只要echo即可。启动时需创建锁文件,停止时需删除锁文件。
- 适当加入条件判断使得脚本更健壮。
#!/bin/bash # # chkconfig: - 50 50 # description: test service script # prog=$(basename $0) lockfile="/var/lock/subsys/$prog" case $1 in start) if [ -e $lockfile ]; then echo "the service $prog has already started." else touch $lockfile echo "the service $prog starts finished." fi ;; stop) if [ ! -e $lockfile ]; then echo "the service $prog has already stopped." else rm -f $lockfile echo "the service $prog stops finished." fi ;; restart) if [ -e $lockfile ]; then rm -f $lockfile touch $lockfile echo "the service $prog restart finished." else touch $lockfile echo "the service $prog starts finished." fi ;; status) if [ -e $lockfile ]; then echo "the service $prog is running." else echo "the service $prog is not running." fi ;; *) echo "usage: $prog {start|stop|restart|status}" exit 1 ;; esac
脚本编写完成后,要放入服务脚本所在的目录、给予权限、加入服务管控(chkconfig),最后就可以使用service命令进行测试了。
~]# cp -av case_service.sh /etc/rc.d/init.d/case_service ‘case_service.sh’ -> ‘/etc/rc.d/init.d/case_service’ ~]# chmod a+x /etc/rc.d/init.d/case_service ~]# chkconfig --add case_service ~]# chkconfig case_service on
像这个服务类的脚本,我们在重启时,可能执行先停止后启动,也可能执行启动。这些在启动和停止时都有已经写好的代码了。如果可以将代码进行重用的话,就可以减少很多代码劳动。这就是之后要介绍的函数。