shell脚本之三:结构化命令case和for、while循环

1、流程控制语句:case

 控制语句即用来实现对程序流程的选择、循环、转向和返回等进行控制。

 case 语句和 if...elif...else 语句一样都是多分支条件语句,case语句主要适用于以下情况,某个变量存在多种取值,需要对其中的每一种取值分别执行不同的命令序列。这种情况与多分支的if语句非常相似,只不过if语句需要判断多个不同的条件,而case语句只是判断一个变量的不同取值。

 case是根据变量的不同取值进行比较,然后针对不同的取值分别执行不同的命令操作

1.1、格式:

 适用于多分支,是一个多选择语句

 case     变量值     in

               模式1

                      命令序列1

                   ;;

               模式2

                      命令序列2

                     ;;

                      ……

                      *  

          ;;

                      默认命令序列

  esac

 case语句的执行流程

 

 case条语句需要注意以下内容:

 case 语句会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序;如果数值不符,则依次比较下一个值;如果所有的值都不符合,则执行"*)",("*"代表所有其他值)中的程序。

 case 语句以"case"开头,以"esac"结尾。

 在每个分支程序之后要以";;"(双分号)结尾,代表该程序段结束(千万不要忘记)。 

 注意,多分支 case 条件语句只能判断变量中的值到底是什么,而不能像多分支if语句那样,可以判断多个条件,所以多分支 case 语句更加适合单条件多分支的情况。比如,我们在系统中经常看到请选择"yes/no",或在命令的输出中选择是执行第一个选项,还是执行第二个选项(fdisk 命令)。在这些情况下,使用 case 最为适合。我们写一个选择"yes/no"的例子,命令如下:

 

1.2、执行流程:

 首先使用“变量值”与模式1进行比较,若取值相同则执行模式1后的命令序列,直到遇见双分号“;;”后跳转至esac,表示分支结束;若与模式1不相匹配,则继续与模式2 进行比较,若取值相同则执行模式2 后的命令序列,直到遇见双分号“;;”后跳转至esac,表示结束分支,……依次类推,若找不到任何匹配的值,则执行默认模式“ *”后的命令序列,直到遇见esac后结束分支

 注意事项:

 取值后面必须为单词in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;

 匹配中的值可以是多个值,通过“|”来分隔

 取值将检测匹配每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令

 它需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号,用两个分号表示该程序段结束。

1.2.2、 范例1:

用户输入0-9任意一个数字,通过case来判断用户输入的是哪一个数字?

 [root@localhost ~]# cat case-1.sh

 #!/bin/bash

 read -p "Type a num ==> " NUM

 case $NUM in

  1)

            echo "Input a num is 1 "

            ;;

  2)

            echo "Input a num is 2 "

            ;;

  [3-8])

            echo "Input a num is $NUM"

            ;;

  9|0)

            echo "Input a num is $NUM"

            ;;

  *)

            echo "Please input [0-9]"

 esac

 说明:

 正则表达式“[]”匹配中括号中指定的任意一个字符,而且只匹配一个字符。

 正则表达式“|”表示或者的意思。

  

 [root@localhost ~]# sh case-1.sh

 Type a num ==> 2

 Input a num is 2

 [root@localhost ~]# sh case-1.sh

 Type a num ==> 6

 Input a num is 6

 [root@localhost ~]# sh case-1.sh

 Type a num ==> 0

 Input a num is 0

 [root@localhost ~]# sh case-1.sh

 Type a num ==> aaa

 Please input [0-9]

1.2.3、 范例2:启动HTTPD服务

 [root@localhost ~]# cat case-http.sh

 #!/bin/bash

 case $1 in

  start)

            systemctl $1 httpd

            ps -ef|grep -v grep |grep "\<httpd\>" &> /dev/null

            if [ $? -eq 0 ];then

                     echo "HTTPD is start...."

            fi

            ;;

  stop)

            systemctl $1 httpd

            echo "httpd stop...."

            ;;

  status)

            systemctl $1 httpd

            ;;

  restart)

            systemctl $1 httpd

            ;;

  *)

            echo "USAGE:`basename $0` start|stop|status|restart"

 esac

 说明:bashname命令用于获取路径中的文件名或路径名(获取的是叶子节点的元素内容)

1.3、  shell中色彩的处理

 shell脚本中echo显示内容带颜色显示,echo显示带颜色,需要使用选项-e(可以用来识别我们的转义字符)

 格式:

     echo -e “\033[背景颜色;文字颜色m  文字内容\033[0m”

     echo -e “\e[背景颜色;文字颜色m  文字内容\e[0m”

 例子:

 

 其中,"033"是八进制数,其对应的ascii码也就是ESC。后面的颜色格式为:[背景色;文字颜色m  文字内容

 0m是控制选项,表示清除所有格式

 注:

   1、背景颜色和文字颜色之间是英文的分号";"

   2、文字颜色后面有个m

     3、字符串前后可以没有空格,如果有的话,输出也是同样有空格

     4、echo显示带颜色,需要使用参数-e

1.3.4、 下面是文字颜色:

 字颜色:30—37

 echo -e "\033[30m 黑色字 \033[0m"

 echo -e "\033[31m 红色字 \033[0m"

 echo -e "\033[32m 绿色字 \033[0m"

 echo -e "\033[33m 黄色字 \033[0m"

 echo -e "\033[34m 蓝色字 \033[0m"

 echo -e "\033[35m 紫色字 \033[0m"

 echo -e "\033[36m 天蓝字 \033[0m"

 echo -e "\033[37m 白色字 \033[0m"

1.3.5、 下面是背景颜色:

 背景颜色范围:40—47

 echo -e "\033[40;37m 黑底白字 \033[0m"

 echo -e "\033[41;37m 红底白字 \033[0m"

 echo -e "\033[42;37m 绿底白字 \033[0m"

 echo -e "\033[43;37m 黄底白字 \033[0m"

 echo -e "\033[44;37m 蓝底白字 \033[0m"

 echo -e "\033[45;37m 紫底白字 \033[0m"

 echo -e "\033[46;37m 天蓝底白字 \033[0m"

 echo -e "\033[47;30m 白底黑字 \033[0m"

1.3.6、 控制选项:

 最后面控制选项说明

 \033[0m 关闭所有属性

 \033[1m 设置高亮度,加粗

 \033[4m 下划线

 \033[5m 闪烁

 \033[7m 反显

2、循环语句

2.1、为什么需要循环语句

 在实际工作中,经常会遇到某项任务需要多次执行的情况,而每次执行时仅仅是处理的对象不一样,其他命令相同。例如,根据通讯录中的姓名列表创建系统账号;根据服务器清单检查主机的存活状态;根据ip地址黑名单设置拒绝访问的防火墙策略等。

 当面对各种列表重复任务时,使用简单的if语句已经难以满足要求,而顺序编写全部代码更是显得异常繁琐、困难重重。这时候比较好的解决方法是使用循环语句控制脚本程序。

2.2、  for

 使用for循环语句时,需要指定一个变量及取值列表,针对每一个不同的取值重复执行相同的命令序列,直到变量值用完退出循环。在这里,“取值列表”称为for语句的执行条件。

 for 循环是固定循环,也就是在循环时已经知道需要进行几次循环。有时也把 for 循环称为计数循环。

2.2.2、 语法格式:

 for 变量名 in 变量取值列表

 do

   commands

 done

 for语句的执行流程:

 

 

 

 

 在这种语法中,for 循环的次数取决于 in 后面值的个数(以空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。也就是说,假设 in 后面有三个值,for 会循环三次,第一次循环会把值 1 赋予变量,第二次循环会把值 2 赋予变量,以此类推。

 do……done之间的命令序列称为循环体,其中的执行语句需要引用变量以完成相应的任务

2.2.3、 取值列表的多种取值方式

2.2.3.2、 可以直接读取IN后面的值,默认以空格做分隔

 [root@localhost ~]# cat for-1.sh

 #!/bin/bash

 for time in morning noon afternoon evening

 do

  echo "This time is $time!"

 done

2.2.3.3、 列表中的复杂值,注意双引号和转义字符的使用

 [root@localhost ~]# cat for-2.sh

 #!/bin/bash

 for i in a b "hello world" c "d e"

 do

  echo the text is $i

 done

 [root@localhost ~]# sh for-2.sh

 the text is a

 the text is b

 the text is hello world

 the text is c

 the text is d e

 [root@localhost ~]# cat for-3.sh

 #!/bin/bash

 for i in a b "li si" c It\'s man

 do

  echo the test is $i

 done

 [root@localhost ~]# sh for-3.sh

 the test is a

 the test is b

 the test is li si

 the test is c

 the test is It's

 the test is man

2.2.3.4、 从变量中取值

 [root@localhost ~]# cat for-4.sh

 #!/bin/bash

 list="a1 b1 c1 d1"

 for i in $list

 do

  echo $i

 done

 [root@localhost ~]# sh for-4.sh

 a1

 b1

 c1

 d1

2.2.3.5、 从命令中取值

 [root@localhost ~]# cat for-5.sh

 #!/bin/bash

 for i in `cat /etc/hosts`    //反撇号

 do

  echo $i

 done

 [root@localhost ~]# sh for-5.sh

 127.0.0.1

 localhost

 localhost.localdomain

 localhost4

 localhost4.localdomain4

 ::1

 localhost

 localhost.localdomain

 localhost6

 localhost6.localdomain6

2.2.4、 字段分隔符

     默认情况下,base shell会把空格,制表符作为默认分隔符,我们也可以通过IFS来指定其他符号做为分隔符,例如换行符作为分隔符。

 [root@localhost ~]# cat for-6.sh

 #!/bin/bash

 OLD_IFS=$IFS

 IFS=$’\n’                #$’\n’表示换行符

 for i in `cat /etc/hosts`

 do

  echo $i

 done

 IFS=$OLD_IFS

 [root@localhost ~]# sh for-6.sh

 127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

 ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

for循环应用实例

 实例1:根据姓名列表批量添加用户

 根据人事部门给出的员工姓名的拼音列表,在linux服务器中添加相应的用户账户,初始密码设置为123456,其中员工姓名列表中的账号数据并不固定,而且除了要求账号名称是拼音外,并无其他特殊规律。

 针对上述要求,可先指定员工列表文件users.txt,然后编写一个名为uaddfor.sh的shell脚本,从users.txt文件中读取用户名称,重复执行添加用户、设置初始密码的相关操作。

 [root@localhost ~]# cat /root/users.txt

 yueyunpeng

 guodegang

 yuqian

 lixiaolu

 创建脚本,脚本内容如下:

 [root@localhost ~]# cat /root/uaddfor.sh

 #!/bin/bash

 PASSWORD="123456"

 ULIST=$(cat /root/users.txt)

 for UNAME in $ULIST

 do

  useradd $UNAME

  echo "$UNAME:$PASSWORD" | chpasswd

 done

 实例2:根据ip地址列表检查主机状态

 根据包含公司服务器ip地址的列表文件,检查其中各主机的ping连通性,输出各主机是否启动、关闭。

 ip列表文件:

 [root@localhost ~]# cat ipadds.txt

 192.168.30.11

 192.168.30.12

 192.168.30.13

 编写脚本,脚本内容如下

 [root@localhost ~]# cat chkhosts.sh

 #!/bin/bash

 HLIST=$(cat /root/ipadds.txt)

 for ip in $HLIST

 do

  ping -c 3 -i 0.2 -W 3 $ip &> /dev/null

  if [ $? -eq 0 ]

  then

   echo "Host $ip is up."

  else

   echo "Host $ip is down."

  fi

 done

 说明:ping命令相关选项

 -c :发送Ping包的数量;

 -i : 设定间隔几秒发送一个ping包,默认一秒ping一次

 -W:等待响应的时间,以秒计。该选项只影响在没有任何响应的情况下退出

2.2.5、 数字列表的获取方式 seq

2.2.5.1、 补全

 -w  补全

 [root@localhost ~]# seq -w 6 10   意思是如果最大数是两位,不足两位的用0

 06

 07

 08

 09

 10

2.2.5.2、 步长,跨度

 [root@localhost ~]# seq 2 2 7  从2开始计算,每次加2,最大到7

 2

 4

 6

2.2.6、 shell中使用

 [root@localhost ~]# cat for-10.sh

 #!/bin/bash

 for i in {5..10}

 do

  echo $i

 done

  

 echo =============

  

 for i in `seq 2 8`

 do

  echo $i

 done

2.3、  while

 重复判断条件测试操作,只要条件成立就反复执行对应的命令序列(循环体),直到条件测试不成立或为假;

使用while循环语句时,可以根据特定的条件反复执行一个命令序列,直到该条件不再满足时为止。在脚本应用中,应该避免出现死循环的情况,否则后面的命令操作将无法执行。因此,循环体内的命令序列中应包括修改测试条件的语句,以便在适当的时候使测试条件不再成立,从而结束循环。

语法格式:

 while 条件测试操作

 do

 命令序列

 done

 while语句执行流程

 

 注意:避免陷入死循环, 条件测试设置为while true 或while false ,循环退出根据测试条件的退出码来定,一定要设置退出条件

范例1:

 [root@localhost ~]# cat while-1.sh

 #!/bin/bash

 var=10

 while [ $var -gt 0 ]

 do

  echo $var

  var=$[$var-1] #let var-- or var=$(expr $var - 1) or var=$((var-1))

 done

 [root@localhost ~]# sh while-1.sh

 10

 9

 8

 7

 6

 5

 4

 3

 2

 1

范例2:编写脚本用于批量添加用户,要求如下:

 要求提供交互功能,当管理员执行该脚本时,可以根据提示指定需添加的用户数量(少于100)、用户名前缀、并能够设置这些用户账户的失效时间,初始密码。用户名编号统一使用两位数,如使用”01”、”02”、”03”的形式,而不是”1”、”2”、”3”的形式。

 编写对应的批量删除用户脚本,要能够通过命令行参数指定用户名前缀,执行脚本后删除所有使用了该前缀的用户账户,但要防止删除root用户。

 批量添加用户脚本:

 1、批量添加用户脚本myuadd.sh内容如下:

 

 2、给脚本myuadd.sh增加x权限

 3、执行脚本myuadd.sh,添加用户

 4、验证结果:查看/etc/passwd文件是否添了新用户

 批量删除用户脚本:

 1、批量删除用户脚本myudel.sh内容如下:

 

 2、给脚本myudel.sh增加x权限

 3、执行脚本myudel.sh,删除用户。如myudel.sh caiwu03或myudel.sh caiwu

 4、验证结果:查看/etc/passwd文件中已删除的用户是否还存在。

 范例3:猜价格游戏

 在一些娱乐节目中,经常有猜价格的游戏,要求参与者在最短时间内猜出展示商品的实际价格,当所猜的价格高于或低于实际价格,主持人给出相应提示。

 案例要求如下:由脚本预先生成一个随机的价格(0-999)作为实际价格,判断用户猜测的价格是否高于或低于实际价格,给出相应提示后再次要求用户猜测,直到用户猜中实际价格为止,输出用户共猜测的次数、实际价格。

 针对上述要求,主要设计思路如下:通过环境变量RANDOM可获得一个小于216 的随机数,计算其与1000的余数即可获得0~999的随机价格,反复猜测操作可以通过以true作为测试条件的while循环实现,当用户猜中实际价格时终止循环,判断猜测价格与实际价格的过程采用if语句实现,嵌套在while循环体内,使用变量来记录猜测次数。

 

 exit命令说明:系统中是有 exit 命令的,用于退出当前用户的登录状态。但是在 Shell 脚本中,exit 语句是用来退出当前脚本的。也就是说,在 Shell 脚本中,只要碰到了 exit 语句,后续的程序就不再执行,而直接退出脚本。exit 的语法如下:

 exit [返回值]

 如果在 exit 之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询 $? 这个变量来査看返回值。如果 exit 之后没有定义返回值,则脚本执行之后的返回值是执行 exit 语句之前最后执行的一条命令的返回值。

posted @ 2020-08-08 12:31  轩辕吊雷  阅读(797)  评论(0编辑  收藏  举报