关于IT运维技术的
最佳实践博客网站

大量示例:Shell基础之控制流程结构之until和while

一、简单的until循环

以下脚本不断的搜寻who命令中用户root,变量IS-ROOT保存grep命令结果。如果找到了root,循环结束,并向用户simon发送邮件,通知他用户root已经登录,注意这里sleep命令用法,它经常用于until循环中,因为必须让循环体内命令睡眠几秒钟再执行,否则会消耗大量系统资源。

#!/bin/sh
# until_who.sh
IS_ROOT=`who | grep root`
until [ "$IS_ROOT" ]
do
        sleep 5
done    
echo "Watch it. roots in " | mail simon

二、until实现监视文件

下面例子中, until循环不断挂起做睡眠,直至文件/tmp/monitor.lck被删除。文件删除后,脚本进入正常处理过程。

#!/bin/sh
# until_lck.sh
LOCK_FILE=/tmp/process.LCK
until [ ! -f $LOCK_FILE ]
do
        sleep 1
done    
echo "file deleted "
# normal processing now, file is present

上述例子是使脚本与其他处理过程协调工作的一种方法。还有另外一种方法使脚本间互相通信。假定有另一段脚本process.main用于搜集本地网络所有机器的信息并将之放入一个报表文件。当脚本process.main运行时,创建了一个LCK文件(锁文件),上面脚本必须接收process.main搜集的信息,但是如果process仍然在修改报表文件时试图处理该文件就不太好了。为克服这些问题,脚本process.main创建了一个LCK文件,当它完成时,就删除此文件。上述脚本将挂起,等待LCK文件被删除,一旦LCK文件删除,上述脚本即可处理报表文件。

三、until实现监视磁盘空间

until循环做监视条件也很有用。假定要监视文件系统容量,当它达到一定水平时通知超级用户。下面的脚本监视文件系统/logs,不断从变量$LOOK_OUT中抽取信息, $LOOK_OUT包含使用awk和grep得到的/logs容量。如果容量达到90%,触发命令部分,向超级用户发送邮件,脚本退出。必须退出,如果不退出,条件保持为真(例如,容量总是保持在90%以上),将会不断的向超级用户发送邮件。

#!/bin/sh
# until_mon.sh
# get present column and strip off header row from df
LOOK_OUT=`df | grep /logs | awk '{print $5}' | sed 's/%//g'`
echo $LOOK_OUT
until [ "$LOOK_OUT -gt "90" ]
do
        echo "Filesystem..logs is nearly full" | mail root
        exit 0
done

四、简单的while循环

以下是一个基本的while循环,测试条件是:如果COUNTER小于5,那么条件返回真。COUNTER从0开始,每次循环处理时, COUNTER加1。

#!/bin/sh
# whilecount.sh
COUNTER=0
# does the counter = 5 ?
while [ $COUNTER -lt 5 ]
do
        # add ono to the counter
        COUNTER=`expr $COUNTER + 1`
        echo $COUNTER
done

运行上述脚本,返回数字1到5,然后终止

五、使用while循环读键盘输入

while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按结束循环。

#!/bin/sh
# whileread.sh
echo "type <CTRL-D> to terminate"
echo -n "Enter your most liked file :"
while read FILE
do
        echo "Yeah, great film the $FILE"
done

程序的运行结果如下:

type <CTRL-D> to terminate
Enter your most liked file :Sound of Music
Yeah, great film the Sound of Music
<CTRL-D>

六、用while循环从文件中读取数据

假定要从names.txt下面包含雇员名字、从属部门及其ID号的一个文件中读取信息。可以用一个变量保存每行数据,当不再有读取数据时条件为真。while循环使用输入重定向以保证从文件中读取数据。注意整行数据被设置为单变量$LINE。

names.txt内容如下:

Louise Conrad:Accounts:ACC8987
Peter James:Payroll:PR489
Fred Terms:Customer:CUS012
James Lenod:Accounts:ACC887
Frank Pavely:Payroll:PR489

脚本如下:

#!/bin/sh
# whileread.sh
while read LINE
do
        echo $LINE
done < names.txt

程序的运行结果如下:

[root@localhost ~]# ./whileread.sh 
Louise Conrad:Accounts:ACC8987
Peter James:Payroll:PR489
Fred Terms:Customer:CUS012
James Lenod:Accounts:ACC887
Frank Pavely:Payroll:PR489

七、while使用IFS读文件,去除域分隔符

输出时要去除冒号域分隔符,可使用变量IFS。在改变它之前保存IFS的当前设置。然后在脚本执行完后恢复此设置。使用IFS可以将域分隔符改为冒号而不是空格或tab键。这里有3个域需要加域分隔,即NAME、DEPT和ID。为使输出看起来更清晰,对echo命令使用tab键将域分隔得更开一些,脚本如下:

#!/bin/sh
# whilereadifs
# save the setting of IFS
SAVEDIFS=$IFS
# assign new separator to IFS
IFS=:
while read NAME DEPT ID
do
        echo -e "$NAME\t $DEPT\t $ID"
done < names.txt
# restore the settings of IFS
IFS=$SAVEDIFS

脚本运行的输出结果如下:

Louise Conrad    Accounts        ACC8987
Peter James      Payroll         PR489
Fred Terms       Customer        CUS012
James Lenod      Accounts        ACC887
Frank Pavely     Payroll         PR489

八、while实现带有测试条件的文件处理

大部分while循环里都带有一些测试语句,以决定下一步的动作。这里从人员文件中读取数据,打印所有细节到一个保留文件中,直至发现James Lenod,脚本退出。测试前反馈的信息要确保”James Lenod”加入保留文件中。注意,所有变量在脚本顶端被设置完毕。这样当不得不对变量进行改动时可以节省时间和输入。所有编辑都放在脚本顶端,而不是混于整个脚本间。脚本如下:

#!/bin/sh
# whileread_file.sh
# initialise variables
SAVEDIFS=$IFS
IFS=:
HOLD_FILE=hold_file
NAME_MATCH="James Lenod"
INPUT_FILE=names.txt
# create a new HOLD_FILE each time, in case script is continuously run
>$HOLD_FILE
while read NAME DEPT ID
do
        # echo all information into holdfile with redirection
        echo $NAME $DEPT $ID >> $HOLD_FILE
        # is it a match ???
        if [ "$NAME" == "$NAME_MATCH" ]; then
                # yes then nice exit
                echo "all entries up to and including $NAME_MATCH are in $HOLD_F
ILE"
                exit 0
        fi      
done < $INPUT_FILE
# restore IFS
IFS=$SAVEDIFS

还可以采取进一步动作,列出多少个雇员属于同一部门。这里保持同样的读方式。假定每个域都有一个变量名,然后在case语句里用expr增加每行匹配脚本。任何发现的未知部门知识反馈到标准错误中,如果一个无效部门出现,没有必要退出.

#!/bin/sh
# whileread_cond.sh
# initialise variables
ACC_LOOP=0; CUS_LOOP=0; PAY_LOOP=0;
SAVEDIFS=$IFS
IFS=:
while read NAME DEPT ID
do
        # increment counter for each matched dept.
        case $DEPT in
                Accounts) ACC_LOOP=`expr $ACC_LOOP + 1`
                ACC="Accounts"
                ;;
                Customer) CUS_LOOP=`expr $CUS_LOOP + 1`
                CUS="Customer"
                ;;
                Payroll) PAY_LOOP=`expr $PAY_LOOP + 1`
                PAY="Payroll"
                ;;
                *) echo "`basename $0`: Unknown department $DEPT" >&2
                ;;
        esac
done < names.txt
IFS=$SAVEDIFS
echo "there are $ACC_LOOP employees assigned to $ACC dept"
echo "there are $CUS_LOOP employees assigned to $CUS dept"
echo "there are $PAY_LOOP employees assigned to $PAY dept"

程序运行的结果如下所示:

[root@localhost ~]# ./whileread_cond.sh   
there are 2 employees assigned to Accounts dept
there are 1 employees assigned to Customer dept
there are 2 employees assigned to Payroll dept
[root@localhost ~]# vim whileread_cond.sh

九、while实现扫描文件行来进行数目统计

文件包含有部门STAT和GIFT所卖的商品数量,文件内容如下:

[root@localhost ~]# cat total.txt 
STAT    3444
GIFT     233
GIFT     252
GIFT     932
STAT     212
STAT     923
GIFT     129

现在的任务是要统计部门GIFT所卖的各种商品数量,脚本如下:

#!/bin/sh
# total.sh
# init variables
LOOP=0
TOTAL=0
COUNT=0
echo "Items Dept"
echo "____________"
while read DEPT ITEMS
do
        # keep a count on total records read
        COUNT=`expr $COUNT + 1`
        if [ "$DEPT" == "GIFT" ]; then
                # keep a running total
                TOTAL=`expr $TOTAL + $ITEMS`
                ITEMS=`expr $ITEMS + 1`
                echo -e "$ITEMS\t$DEPT"
        fi      
        # echo $DEPT $ITEMS
done < total.txt
echo "============"
echo $TOTAL
echo "There were $COUNT entries altogether in the file"

脚本的运行结果如下:

Items Dept
____________
234     GIFT
253     GIFT
933     GIFT
130     GIFT
============
1546
There were 7 entries altogether in the file

十、while实现忽略#字符开始的行

假定要使用一般的while循环读一个配置文件,可拣选每一行,大部分都是实际操作语句。有时必须忽略以一定字符开头的行,这时需要用case语句,因为#是一个特殊字符,最好首先用反斜线屏蔽其特殊意义,在#符号后放一个星号*,指定*后可包含任意字符,文本内容如下:

[root@localhost ~]# cat config
# THIS IS THE SUB SYSTEM AUDIT CONFIG FILE
# DO NOT EDIT!!!!.IT WORKS
#
# type of admin access
AUDITSCH=full
# lanuch place of sub-systems
AUDITSUB=/usr/opt/audit/sub
# serial hash number of product
HASHSER=12890AB3
# END OF CONFIG FILE!!!

实现脚本如下:

#!/bin/sh
# ignore_hash.sh
INPUT_FILE=config
if [ -s $INPUT_FILE ]; then
        while read LINE
        do      
                case $LINE in
                        \#*);;
                        # ignore any hash signs
                        *) echo $LINE
                        ;;
                esac    
        done < $INPUT_FILE
else    
        echo "`basename $0` : Sorry $INPUT_FILE does not exist or is empty"
        exit 1
fi

程序的输出结果如下:

AUDITSCH=full
AUDITSUB=/usr/opt/audit/sub
HASHSER=12890AB3

十一、处理格式化报表

一个常用任务是将不想要的行剔除。以下是库存商品水平列表,我们感兴趣的是那些包含商品记录当前水平的列.

[root@localhost ~]# cat order 
################ RE-ORDER REPORT  ##############
ITEM           ORDERLEVEL              LEVEL
####################################################
Pens            14                              12
Pencils         15                              15
Pads             7                               3
Disks            3                               2
Sharpeners       5                               1
####################################################

我们的任务是读取其中取值,决定哪些商品应重排。如果重排,重排水平应为现在商品的两倍。输出应打印需要重排的每种商品数量及重排总数。我们已经知道可以忽略以某些字符开始的行,因此这里没有问题。首先读文件,忽略所有注释行和以’ITEM’开始的标注行。读取文件至一临时工作文件中,为确保不存在空行,用sed删除空行,需要真正做的是过滤文本文件。脚本如下:

#!/bin/sh
# whileorder.sh
INPUT_FILE=order
HOLD=order.tmp
if [ -s $INPUT_FILE ]; then
        # zero the output file, we do not want to append!
        >$HOLD
        while read LINE
        do      
                case $LINE in
                        \#*|ITEM*);; # ignore any # or the line with ITEM
                        *)
                        # redirect the output to a temp file
                        echo $LINE >>$HOLD
                        ;;
                esac    
        done <$INPUT_FILE
        # use to sed to delete any empty lines, if any
        sed -e '/^$/d' order.tmp >order.$
        mv order.$ order.tmp
else    
        echo "`basename $0` : Sorry $INPUT_FILE does not exist or empty"
fi

执行脚本后,输出结果为:

[root@localhost ~]# cat order.tmp 
Pens 14 12
Pencils 15 15
Pads 7 3
Disks 3 2
Sharpeners 5 1

十二、expr对数字进行数值运算

根据十一中的示例,对文件进行expr运算,脚本如下:

#!/bin/sh
# whileorder2
# init the variables
HOLD=order.tmp
RE_ORDER=0
ORDERS=0
STATIONERY_TOT=0
if [ -s $HOLD ]; then
        echo "========= STOCK RE_ORDER REPORT ========="
        while read ITEM RECORD LEVEL
        do
                # are we below the reorder level for this item ??
                if [ "$LEVEL" -lt "$RECORD" ];then
                        # yes, do the new order amount
                        NEW_ORDER=`expr $RECORD + $RECORD`
                        # running total of orders
                        ORDERS=`expr $ORDERS + 1`
                        # running total of stock levels
                        STATIONERY_TOT=`expr $STATIONERY_TOT + $LEVEL`
                        echo "$ITEM need reordering to the amount $NEW_ORDER"
                fi
        done <$HOLD
        echo "$ORDERS new items need to be ordered"
        echo "Our reorder total is $STATIONERY_TOT"
else
        echo "`basename $0` : Sorry $HOLD does not exists or is empty"
fi

脚本运行所得输出结果:

========= STOCK RE_ORDER REPORT =========
Pens need reordering to the amount 28
Pads need reordering to the amount 14
Disks need reordering to the amount 6
Sharpeners need reordering to the amount 10
4 new items need to be ordered
Our reorder total is 18
赞(0)
未经允许不得转载:菜鸟HOW站长 » 大量示例:Shell基础之控制流程结构之until和while
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址