一、简单的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