Shell脚本for、while、case语句应用

一、使用for循环语句

        在实际工作中,经常会遇到某项任务需要多次执行的情况,而每次执行时仅仅是处理的对象不一样,其他命令相同。例如,根据通讯录中的姓名列表创建系统账号,根据服务器清单检查各主机的存活状态,根据IP地址黑名单设置拒绝访问的防火墙策略等。
        当面对各种列表重复任务时,使用简单的if语句已经难以满足要求,而顺序编写全部代码更是显得异常烦琐,困难重重。本节将要学习的for循环语句,可以很好地解决类似问题。

1. for语句的结构

        使用for循环语句时,需要指定一个变量及可能的取值列表,针对每个不同的取值重复执行相同的命令序列,直到变量值用完退出循环。在这里,“取值列表”称为for语句的执行条件,其中包括多个属性相同的对象,需要预先指定(如通讯录、IP黑名单)。

语法:

for 变量名 in 取值列表
do
   命令序列
done
2.for语句应用
1) 根据姓名列表批量添加用户

        根据人事部门给出的员工姓名的拼音列表,在Linux服务器中添加相应的用户账号,初始密码均设置为“123456”。其中,员工姓名列表中的账号数量并不固定,而且除了要求账号名称是拼音之外,并无其他特殊规律。
        针对上述要求,可先指定员工列表文件users、txt,然后编写一个名为uaddfor.sh的Shell脚本,从users.txt文件中读取各用户名称,重复执行添加用户、设置初始密码的相关操作。

[root@localhost ~]# vim uaddfor.sh
#!/bin/bash
ULIST=$(cat /root/users.txt)
for UNAME in $ULIST
do
   useradd $UNAME
   echo "123456" | passwd -- stdin $UNAME &>/dev/null
done
[root@localhost ~]# chmod +x uaddfor.sh       //执行权限
[root@localhost ~]# ./uaddfor.sh              //测试并确认执行结果
[root@localhost ~]# tail -3 /etc/passwd

        若要删除 uaddfor.sh脚本所添加的用户,只需参考上述脚本代码,将for循环体中添加用户的命令
序列改为删除用户的操作即可。例如,建立一个名为udelfor.sh的脚本如下所示。

[root@localhost ~]# vim udelfor.sh
#!/bin/bash
ULIST=$ (cat /root/users.txt)
for UNAME in $ULIST
do
   userdel -r $UNAME &>/dev/null
done
[root@localhost ~]# chmod +x udelfor.sh
[root@localhost ~]# ./udelfor.sh
[root@localhost ~]# id chenye
id: chenye: no such user
2) 根据IP地址列表检查主机状态

        根据包含公司各服务器IP地址的列表文件,检查其中各主机的ping连通性(测试方法可参考上一章中的pinghost.sh脚本),输出各主机是否启动、关闭。其中,服务器的数量并不固定,各服务器的IP地址之间也无特殊规律。
        针对此案例要求,可先指定IP地址列表文件 ipadds.txt,然后编写一个名为chkhosts.sh的 Shell脚本,从ipadds.txt文件中读取各服务器的IP地址,重复执行ping连通性测试,并根据测试结果输出相应的提示信息。

[root@localhost ~]# vim /root/ipadds.txt
172.16.16.1
172.16.16.22
172.16.16.220
[root@localhost ~]# vim 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
[root@localhost ~]# chmod +x chkhosts.sh
[root@localhost ~]# ./chkhosts.sh
Host 172.16.16.1 is up.
Host 172.16.16.22 is up.
Host 172.16.16.220 is down.

二、使用while循环语句

        for 循环语句非常适用于列表对象无规律,且列表来源已固定(如某个列表文件)的场合。而对于要求控制循环次数、操作对象按数字顺序编号、按特定条件执行重复操作等情况,则更适合使用另外一种循环--while语句。

1. while语句的结构

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

语句:

while 条件测试操作
do
   命令序列
done

        使用while循环语句时,有两个特殊的条件测试操作,即true(真)和false(假)。使用true作为条件时,表示条件永远成立,循环体内的命令序列将无限执行下去,除非强制终止脚本(或通过应用 -- Linux高级管理exit语句退出脚本):反之,若使用false作为条件,则循环体将不会被执行。

2.while语句应用

        为了进一步理解while语句的结构和流程,掌握while语句在脚本中的实际使用,下面依次介绍两个脚本示例。

1)批量添加规律编号的用户

        在一些技术培训和学习领域,出于实验或测试的目的,需要批量添加用户账号,这些用户的名称中包含固定的前缀字串,并按照数字顺序依次进行编号,账号的数量往往也是固定的。例如,若要添加20个用户,名称依次为 stu、stu、… stu20,可以参考以下操作。

[root@localhost ~]# vim uaddwhile.sh
#!/bin/bash
PREFIX="stu"
i=1
while [ $i -le 20 ]
do
   useradd $ {PREFIX} $i
   echo "123456" | passwd -- stdin ${PREFIX) $i &> /dev/null
   let i++
done
[root@localhost ~]# chmod +x uaddwhile.sh
[root@localhost ~]# ./uaddwhile.sh
[root@localhost ~]# grep "stu" /etc/passwd | tail -3
stu18:x:1022:1022: :/home/stu18:/bin/bash
stul9:x:1023:1023 :: /home/stu19:/bin/bash
stu20:x:1024:1024 :: /home/stu20:/bin/bash

        若要删除 uaddwhile,sh脚本所添加的用户,只需参考上述脚本代码,将while循环体中添加用户的命令序列改为删除用户的操作即可。

[root@localhost ~]# vim udelwhile.sh
#!/bin/bash
PREFIX="stu"
i=1
while [ $i -le 20 ]
do
   userdel -r $ (PREFIX}$i
   let i++
done
[root@localhost ~]# chmod +x udelwhile.sh
[root@localhost ~]# ./udelwhile.sh
[root@localhost ~]# id stu20
id: stu20:无此用户
id: stu20: no such user
2) 猜价格游戏

        中央电视台著名的“时尚购物街”节目中,有一个猜价格的互动环节,要求参与者在最短的时间内猜出展示商品的实际价格,当所猜的价格高出或低于实际价格时,主持人会给出相应的提示。下面以此环节为原型,编写一个猜价格的Shell脚本。
        案例要求如下:由脚本预先生成一个随机的价格数目(0~999)作为实际价格,判断用户猜测的价格是否高出或低于实际价格,给出相应提示后再次要求用户猜测:一直到用户猜中实际价格为止,输出用户共猜测的次数、实际价格。
        针对上述要求,主要设计思路如下:通过环境变量RANDOM可获得一个小于2”的随机整数,计算其与1000的余数即可获得0~999的随机价格:反复猜测操作可以通过以true作为测试条件的while循环实现,当用户猜中实际价格时终止循环;判断猜测价格与实际价格的过程采用if语句实现,嵌套在while循环体内;使用变量来记录猜测次数。

[root@localhost ~]# vim pricegame.sh
#!/bin/bash
PRICE=$ (expr $RANDOM % 1000)
TIMES=0
echo"商品实际价格范围为0-999,猜猜看是多少?”
while true
do
   read-p“请输入你猜测的价格数目:”INT
   let TIMES++
   if [ $INT -eq $PRICE ] ; then
      echo“恭喜你答对了,实际价格是 $PRICE"
      echo “你总共猜测了 STIMES 次”
      exit 0
   elif [ $INT -gt $PRICE ] ; then
      echo"太高了!”
   else
      echo"太低了!”
   fi
done
[root@localhost ~]# chmod +x pricegame.sh

测试

[root@localhost ~]# ./pricegame.sh
商品实际价格范围为0-999,猜猜看是多少?
请输入你猜测的价格数目:714
太高了!
请输入你猜测的价格数目:154
太低了!
请输入你猜测的价格数目:455
太高了!
请输入你猜测的价格数目:350
太高了!
请输入你猜测的价格数目:300
太高了!
请输入你猜测的价格数目:265
太高了!
请输入你猜测的价格数目:253
恭喜你答对了,实际价格是 253
你总共猜测了 7 次

三、使用case分支语句

       学习多分支的if语句时,曾经提到过改用case语句可以使脚本程序的结构更加清晰、层次分明,本节就来学习case语句的语法结构及应用。

1. case语句的结构

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

语句:

case 变量值 in
模式1)
    命令序列1
    ;;
模式2)
    命令序列2
    ;;
    ......
*)
    默认命令序列
esac

使用case分支语句时,有几个值得注意的特点如下所述。

  • case行尾必须为单词“in”,每一模式必须以右括号“)”结束。
  • 双分号“ ;; ”表示命令序列的结束。
  • 模式字符串中,可以用方括号表示一个连续的范围,如“[0-9]”;还可以用竖杠符号“|”表示或,如“AIB”。
  •  最后的“*)”表示默认模式,其中的*相当于通配符。
2. case 语句应用示例

        为了进一步理解case语句的结构和流程,掌握case语句在脚本中的实际使用,下面依次介绍两个脚本示例。

1)检查用户输入的字符类型

        提示用户从键盘输入一个字符,通过case语句判断该字符是否为字母,数字或者其他控制字符,并给出相应的提示信息。

[root@localhost ~]# vim hitkey.sh
#!/bin/bash
read-p"请输入一个字符,并按Enter键确认:" KEY
case "$KEY" in
  [a-z] | [A-2])
     echo“您输入的是 字母.”
     ;;
  [0-9])
     echo“您输入的是 数字.”
     ;;
  *)
     echo“您输入的是 空格、功能键或其他控制字符.”
esac
[root@localhost ~]# chmod +x hitkey.sh

测试

[root@localhost ~]# ./hitkey.sh
请输入一个字符,并按Enter键确认:k
您输入的是 字母。
[root@localhost ~]# ./hitkey.sh
请输入一个字符,并按Enter键确认:8
您输入的是 数字。
[root@localhost ~]# ./hitkey.sh
请输入一个字符,并按Enter键确认:^[[19~
您输入的是 空格、功能键或其他控制字符。
2) 编写系统服务脚本

        编写一个名为myprog的系统服务脚本,通过位置变量$1指定的 start、stop、restart、status控制参数,分别用来启动、停止、重启sleep进程,以及查看sleep进程的状态。其中,命令sleep用来暂停指定秒数的时间,这里仅用做测试,在实际运维工作中应将sleep改为相应后台服务的控制命令序列。

[root@localhost ~]# vim myprog
#!/bin/bash
case "$1" in
start)
   echo -n"正在启动sleep服务
   if sleep 7200 &
 then
       echo "OK"
   fi
   ;;
stop)
   echo-n"正在停止sleep服务
   pkill "sleep" &> /dev/null
echo "OK"
   ;;
status)
   if pgrep "sleep" &>/dev/null ; then
       echo "sleep服务已经启动.”
   else
      echo "sleep 服务已经停止.”
   fi
   ;;
restart)
   $0 stop
   $0 start
   ;;
*)
   echo "Mi&: $0 {start| stop|status | restart}"
esac

测试

[root@localhost ~]# chmod +x myprog
[root@localhost ~]# ./myprog start
正在启动 sleep 服务 …. OK
[root@localhost ~]# ./myprog status
sleep 服务已经启动.
[root@localhost ~]# ./myprog stop
正在停止sleep服务 …. OK
[root@localhost ~]# ./myprog reload
用法: ./myprog {start | stop | status | restart}

        在Linux 系统中,源码软件包编译安装后提供的服务控制脚本使用了case分支语句;也有一些源码包没有提供服务控制脚本,编译安装后可参照上例自行编写服务控制脚本。平时控制各种系统服务时,提供的start、stop、restart等位置参数,正是由case语句结构来识别并完成相应操作的。有兴趣的同学可自行查阅这些脚本内容。
        若要将 myprog 服务交给systemd来管理,还需要在/lib/systemd/system目录下添加相应的myprog.service配置文件。

你可能感兴趣的:(linux,运维,服务器)