【3.3】 shell script循环

一、while do done, until do done (不定循环)

while [ condition ] <==中括号内的状态就是判断式
do <==do 是循环的开始!
程序段落
done <==done 是循环的结束

while 的中文是『当….时』,所以,这种方式说的是『当 condition 条件成立时,就进行循环,直到condition 的条件不成立才停止』的意思。还有另外一种不定循环的方式:

until [ condition ]
do
程序段落
done

这种方式恰恰与 while 相反,它说的是『当 condition 条件成立时,就终止循环, 否则就持续进行循环的程序段。』是否刚好相反啊~我们以 while 来做个简单的练习好了。 假设我要让使用者输入 yes 或者是 YES才结束程序的执行,否则就一直进行告知用户输入字符串。

[root@www scripts]# vi sh13.sh

#!/bin/bash
# Program:
#       Repeat question until user input correct answer.
# History:
# 2005/08/29   VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
while [ "$yn" != "yes" -a "$yn" != "YES" ]
do
		 read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."

上面这个例题的说明是『当 $yn 这个变量不是 “yes” 且 $yn 也不是 “YES” 时,才进行循环内的程序。』 而如果 $yn 是 “yes” 或 “YES” 时,就会离开循环啰~那如果使用 until 呢?呵呵有趣啰~ 他的条件会变成这样:

[root@www scripts]# vi sh13-2.sh

#!/bin/bash
# Program:
#       Repeat question until user input correct answer.
# History:
# 2005/08/29   VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
 
until [ "$yn" == "yes" -o "$yn" == "YES" ]
do
		 read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."

仔细比对一下这两个东西有啥不同喔! ^_^再来,如果我想要计算 1+2+3+….+100 这个数据呢? 利用循环啊~他是这样的:

[root@www scripts]# vi sh14.sh

#!/bin/bash
# Program:
#       Use loop to calculate "1+2+3+...+100" result.
# History:
# 2005/08/29   VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
 
s=0  # 这是加总的数值变数
i=0  # 这是累计的数值,亦即是 1, 2, 3....
while [ "$i" != "100" ]
do
		 i=$(($i+1))   # 每次 i 都会增加 1
		 s=$(($s+$i))  # 每次都会加总一次!
done
echo "The result of '1+2+3+...+100' is ==> $s"

二、for…do…done (固定循环)

相对于 while, until 的循环方式是必须要『符合某个条件』的状态, for 这种语法,则是『 已经知道要进行几次循环』的状态!他的语法是:

for var in con1 con2 con3 ...
do
程序段
done

以上面的例子来说,这个 $var 的变量内容在循环工作时:

第一次循环时, $var 的内容为 con1 ;

第二次循环时, $var 的内容为 con2 ;

第三次循环时, $var 的内容为 con3 ;

….

我们可以做个简单的练习。假设我有三种动物,分别是 dog, cat, elephant 三种,

我想每一行都输出这样:『There are dogs…』之类的字样,则:

[root@www scripts]# vi sh15.sh
#!/bin/bash
# Program:
#      Using for .... loop to print 3 animals
# History:
# 2005/08/29   VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
 
for animal in dog cat elephant
do
		 echo "There are ${animal}s.... "
done

等你执行之后就能够发现这个程序运作的情况啦!让我们想象另外一种状况,由于系统上面的各种账号都是写在 /etc/passwd 内的第一个字段,你能不能透过管线命令的 cut 捉出单纯的账号名称后,以 id 及 finger分别检查使用者的标识符与特殊参数呢?由于不同的 Linux 系统上面的账号都不一样!此时实际去捉/etc/passwd 并使用循环处理,就是一个可行的方案了!程序可以如下:

[root@www scripts]# vi sh16.sh

#!/bin/bash
# Program
#       Use id, finger command to check system account's information.
# History
# 2009/02/18    VBird   first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd)  # 撷取账号名称
for username in $users               # 开始循环进行!
do
		id $username
		finger $username
done

执行上面的脚本后,你的系统账号就会被捉出来检查啦!这个动作还可以用在每个账号的删除、重整上面呢! 换个角度来看,如果我现在需要一连串的数字来进行循环呢?举例来说,我想要利用 ping 这个可以判断网络状态的指令, 来进行网络状态的实际侦测时,我想要侦测的网域是本机所在的192.168.1.1~192.168.1.100,由于有 100 台主机, 总不会要我在 for 后面输入 1 到 100 吧?此时你可以这样做喔!

root@www scripts]# vi sh17.sh

#!/bin/bash
# Program
#       Use ping command to check the network's PC state.
# History
# 2009/02/18    VBird   first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
network="192.168.1"              # 先定义一个网域的前面部分!
for sitenu in $(seq 1 100)       # seq 为 sequence(连续) 的缩写之意
do
		 # 底下的程序在取得 ping 的回传值是正确的还是失败的!
		ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
		 # 开始显示结果是正确的启动 (UP) 还是错误的没有连通 (DOWN)
		if [ "$result" == 0 ]; then
				echo "Server ${network}.${sitenu} is UP."
		else
				echo "Server ${network}.${sitenu} is DOWN."
		fi
done

上面这一串指令执行之后就可以显示出 192.168.1.1~192.168.1.100 共 100 部主机目前是否能与你的机器连通! 如果你的网域与鸟哥所在的位置不同,则直接修改上头那个 network 的变量内容即可!其实这个范例的重点在 $(seq ..) 那个位置!那个 seq 是连续 (sequence) 的缩写之意!代表后面接的两个数值是一直连续的! 如此一来,就能够轻松的将连续数字带入程序中啰!

最后,让我们来玩判断式加上循环的功能!我想要让用户输入某个目录文件名, 然后我找出某目录内的文件名的权限,该如何是好?呵呵!可以这样做啦~

[root@www scripts]# vi sh18.sh

#!/bin/bash
# Program:
#       User input dir name, I find the permission of files.
# History:
# 2005/08/29   VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
 
# 1. 先看看这个目录是否存在啊?
read -p "Please input a directory: " dir
if [ "$dir" == "" -o ! -d "$dir" ]; then
		 echo "The $dir is NOT exist in your system."
		 exit 1
fi
 
# 2. 开始测试档案啰~
filelist=$(ls $dir)        # 列出所有在该目录下的文件名
for filename in $filelist
do
		 perm=""
		 test -r "$dir/$filename" && perm="$perm readable"
		 test -w "$dir/$filename" && perm="$perm writable"
		 test -x "$dir/$filename" && perm="$perm executable"
		 echo "The file $dir/$filename's permission is $perm "
done

呵呵!很有趣的例子吧~利用这种方式,你可以很轻易的来处理一些档案的特性呢。接下来,让我们来玩玩另一种 for 循环的功能吧!主要用在数值方面的处理喔!

小标题的图示for…do…done 的数值处理 除了上述的方法之外,for 循环还有另外一种写法!语法如下:

for (( 初始值; 限制值; 执行步阶 ))
do
程序段
done

这种语法适合于数值方式的运算当中,在 for 后面的括号内的三串内容意义为:

初始值:某个变量在循环当中的起始值,直接以类似 i=1 设定好;

限制值:当变量的值在这个限制值的范围内,就继续进行循环。例如 i<=100;

执行步阶:每作一次循环时,变量的变化量。例如 i=i+1。

值得注意的是,在『执行步阶』的设定上,如果每次增加 1 ,则可以使用类似『i++』的方式,亦即是 i 每次循环都会增加一的意思。好,我们以这种方式来进行 1 累加到使用者输入的循环吧!

[root@www scripts]# vi sh19.sh

#!/bin/bash
# Program:
#      Try do calculate 1+2+....+${your_input}
# History:
# 2005/08/29   VBird        First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
 
read -p "Please input a number, I will count for 1+2+...+your_input: " nu
 
s=0
for (( i=1; i<=$nu; i=i+1 )) do s=$(($s+$i)) done echo "The result of '1+2+3+...+$nu' is ==> $s"

一样也是很简单吧!利用这个 for 则可以直接限制循环要进行几次呢!

药企,独角兽,苏州。团队长期招人,感兴趣的都可以发邮件聊聊:tiehan@sina.cn
个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn