前文中介绍的for循环,在使用时必须先建立一个元素列表,以决定循环的次数,但是考虑下面这个例子:如果用户输入“quit”就退出程序,否则将用户输入的字符转换为大写,如and转换为AND,exit转换为EXIT。这个例子无法建立元素列表,因为无法确定用户何时输入quit,也就无法确定要循环多少次。要解决这个问题,就需要引入一个新的机制:while和until。while和until尤其适合循环次数未知的情况,当然,对于循环次数已知的情况,它们也同样适用。
一、while的使用方法
while的基本格式为:
while 测试条件; do
语句1
语句2
...
done
它的意思是当条件满足时,就执行循环,直到条件不满足,就退出循环。它和if的用法有些相似,只不过if只执行一次,而while会对条件反复测试,反复执行,直到条件不满足为止。那么如何使条件不满足呢?这就需要在循环体中改变初始的测试条件中相应变量的值,如果不改变,那么就会形成死循环。
二、while循环的实例演示
例1. 现在使用while循环来计算100以内所有正整数的和:
[root@localhost tutor]# vimsum_while.sh
#!/bin/bash Sum=0 Count=1 while [ $Count -le 100 ]; do Sum=$[$Sum+$Count] let Count++ done echo $Sum
[root@localhost tutor]# bashsum_while.sh
5050
例2. 计算100以内所有偶数的和;
[root@localhost tutor]# vimeven_sum_while.sh
#!/bin/bash EvenSum=0 Count=0 while [ $Count -le 100 ]; do letEvenSum+=Count let Count+=2 # 使用+=可以表示自身加上一个数 done echo $EvenSum
[root@localhost tutor]# basheven_sum_while.sh
2550
这个程序还可以写成以下形式:
[root@localhost tutor]# vimeven_sum_while.sh
#!/bin/bash EvenSum=0 Count=0 while [ $Count -le 100 ];do if [$[$Count%2] -eq 0 ];then letEvenSum+=$Count fi let Count++ done echo $EvenSum # 虽然这种写法较前一种方法看起来更复杂,但它的扩展性更好,比如可以做任意奇数的和,3倍数的和等等
[root@localhost tutor]# basheven_sum_while.sh
2550
如果要遍历文件中的每一行,可以使用while循环配合输入重定向的方式,在done后面重定向要读取的文件,其用法如下:
whileread LINE; do
statement1
statement2
...
done< /Path/To/Somefile
例3.如果用户的ID号为偶数,则显示其名称和shell;对所有用户执行此操作:
[root@localhost tutor]# vimread_line.sh
#!/bin/bash while read LINE; do # 这里的read LINE表示读取/etc/passwd这个文件的一行,将读到的内容放进LINE这个变量中 Uid=`echo$LINE | cut -d: -f3` if [ $[$Uid%2]-eq 0 ]; then echo$LINE | cut -d: -f1,7 fi done < /etc/passwd
[root@localhost tutor]# bashread_line.sh
root:/bin/bash daemon:/sbin/nologin lp:/sbin/nologin shutdown:/sbin/shutdown mail:/sbin/nologin uucp:/sbin/nologin games:/sbin/nologin ftp:/sbin/nologin rpc:/sbin/nologin avahi-autoipd:/sbin/nologin nfsnobody:/sbin/nologin haldaemon:/sbin/nologin gdm:/sbin/nologin ntp:/sbin/nologin apache:/sbin/nologin saslauth:/sbin/nologin sshd:/sbin/nologin tcpdump:/sbin/nologin user1:/bin/bash user3:/bin/bash user5:/bin/bash user7:/bin/bash user9:/bin/bash hello:/bin/tcsh hadoop:/bin/bash
例4:转换用户输入的字符为大写,除了quit(遇见quit退出):
[root@localhost tutor]# vimquit_by_user.sh
#!/bin/bash read -p "A string: " String while [ "$String" != 'quit' ]; do echo $String |tr 'a-z' 'A-Z' read -p"Next [quit for exit]: " String done
[root@localhost tutor]# bashquit_by_user.sh
A string: sd gfdg SD GFDG Next [quit for exit]: adf!fdsf@ ADF!FDSF@ Next [quit for exit]: exit EXIT Next [quit for exit]: quit [root@localhost tutor]#
例5、使用while循环统计/etc/rc.d/init.d/functions中#开头的行数;
[root@localhost tutor]# vimnum_lines_while.sh
#!/bin/bash Num=0 while read LINE; do echo $LINE |grep "^[[:space:]]*#" &> /dev/null && let Num++ # 考虑以#开头的,并且考虑以空格和#开头的行 # 使用&&可以代替if done < /etc/rc.d/init.d/functions echo "Total $Num lines start with #."
[root@localhost tutor]# bashnum_lines_while.sh
Total 70 lines start with #.
三、until循环的使用方法
until和while循环很类似,都是当循环次数未知的时候使用。until的用法格式如下:
until 测试条件; do
语句1
语句2
...
done
和while相反,until是当条件满足时,退出循环;而当条件不满足时就执行语句。事实上until和while二者可以互换,当对满足while的条件取反时,就是until。
四、until循环的实例演示
例6. 使用until改写例1,计算100以内所有正整数的和:
[root@localhost tutor]# vimsum_until.sh
#!/bin/bash Sum=0 Count=1 until [ $Count -gt 100 ]; do # while中使用的是le,until和while的条件相反 letSum+=$Count let Count++ done echo $Sum
[root@localhost tutor]# bashsum_until.sh
5050
例7. 计算100以内所有偶数的和以及奇数的和;在一个循环中实现;
[root@localhost tutor]# vimuntil_odd_even.sh
#!/bin/bash SumOdd=0 SumEven=0 i=1 until [ $i -gt 100 ];do if [ $[$i%2]-eq 0 ];then letSumOdd+=$i else letSumEven+=$i fi let i++ done echo " Odd Sum: $SumOdd. Even Sum: $SumEven"
[root@localhost tutor]# bashuntil_odd_even.sh
Odd Sum: 2550. EvenSum: 2500
例8. 使用until改写例4,转换用户输入的字符为大写,除了quit(遇见quit退出):
[root@localhost tutor]# vimquit_until.sh
#!/bin/bash read -p "A String: " String until [ "$String" == 'quit' ]; do # while循环中当$String不等于quit时执行,until与之相反 echo $String |tr 'a-z' 'A-Z' read -p"Next [quit for Exit]: " String done
[root@localhost tutor]# bashquit_until.sh
A String: sdf opq SDF OPQ Next [quit for Exit]: 123#fsd 123#FSD Next [quit for Exit]: Quit QUIT Next [quit for Exit]: quit
例9、每隔5秒查看hadoop用户是否登录,如果登录,显示其登录并退出;否则,显示当前时间,并说明hadoop尚未登录:
这里需要引入一个新命令sleep,这个命令表示休眠,后面的参数接秒数
[root@localhost tutor]# mansleep
SLEEP(1) UserCommands SLEEP(1)
NAME
sleep - delayfor a specified amount of time
SYNOPSIS
sleep NUMBER[SUFFIX]...
如果要确定当前登陆的用户,可以使用who命令,而要确定hadoop用户是否登陆了,可以使用grep命令:
[root@localhost tutor]# vimhadoop_login.sh
#!/bin/bash until who | grep "^hadoop" &> /dev/null; do # 使用who命令可以查看当前登陆的用户 # 由于who是个命令,而不是一个表达式,所以不需要[] date sleep 5 done echo "Welcom back, hadoop!"
为了更好的理解上述程序的逻辑,可以将其改写为:
[root@localhost tutor]# vimhadoop_login.sh
#!/bin/bash who | grep "^hadoop" &> /dev/null RetVal=$? # 先执行一次 who 和 grep命令 # 然后取出执行状态返回值 until [ $RetVal-eq 0 ]; do # 如果执行状态返回值为0,表示who和grep命令执行成功了,就停止执行循环体 date sleep 5 who | grep"^hadoop" &> /dev/null Retval=$? # 如果date和sleep命令执行了,就再执行一次who和grep命令,以检查hadoop用户是否登陆 done echo "Welcomback, hadoop!"
[root@localhost tutor]# bashhadoop_login.sh
Sat Aug 16 09:55:37 EDT 2014 Sat Aug 16 09:55:42 EDT 2014 Sat Aug 16 09:55:47 EDT 2014 # 此时开启另一个终端,用Hadoop用户登陆 Welcom back, hadoop!
例10. 写一个脚本:
1) 显示一个菜单给用户:
d|D) show disk usages.
m|M) show memory usages.
s|S) show swap usages.
*) quit.
2) 当用户给定选项后显示相应的内容;
当用户选择完成,显示相应信息后,不退出;而让用户再一次选择,再次显示相应内容;除了用户使用quit;
[root@localhost tutor]# vimshow_disk.sh
#!/bin/bash cat <<EOF d|D) show disk usages. m|M) show memery usages. s|S) show swap usages. quit) quit EOF read -p "Input your choice [quit means exit]: "Char until [ "$Char" == 'quit' ]; do case $Char in d|D) df -lh ;; m|M) free -m |grep "^Me" ;; s|S) free -m |grep "^Sw" ;; *) echo"Input Errors." ;; esac read -p"Input your choice [quit means exit]:" Char done
[root@localhost tutor]# bashshow_disk.sh
d|D) show disk usages. m|M) show memery usages. s|S) show swap usages. quit) quit Input your choice [quit means exit]: d Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 23G 4.6G 17G 22% / tmpfs 499M 112K 499M 1% /dev/shm /dev/sda1 485M 35M 426M 8% /boot /dev/sdb3 9.9G 36M 9.7G 1% /mydata Input your choice [quit means exit]:M Mem: 996 358 638 0 63 114 Input your choice [quit means exit]:s Swap: 3051 0 3051 Input your choice [quit means exit]:a Input Errors. Input your choice [quit means exit]:quit