shell script 循环(loop)
除了if...then...fi这种条件判断式之外,循环可能是程序当中最重要的一环了。循环可以不断执行某个程序段落,直到用户设置的条件达成为止,所以重点是那个“条件的达成”是什么。除了这种依据判断式达成与否的不定循环之外,还有另外一种已经固定要跑多少次的循环,可称为固定循环。
while do done和until do done(不定循环)
一般来说,不定循环最常见的就是下面这两种状态了。
while [ condition ] //中括号内的条件就是判断式
do //是循环的开始
程序字段 //循环的内容
done //是循环的结束
while的中文是“当......时,在......期间”,所以,这种方式说的是当condition条件成立时,就进行循环,直到condition的条件不成立才停止的意思。
还有另外一种不定循环的方式
until [ condition ]
do
程序字段
done
这种方式恰恰与while相反,他说的是当conditiong条件成立时,就终止循环,否则就继续执行循环的程序段。
我们以while来做一个简单的练习,假设我要让用户输入yes或者是YES才结束程序的执行,否则就一直进行告知用户输入合法的字符串。
脚本sh13.sh内容
#!/bin/bash
#Program
# Repeat question until user input correct answer.
#History:
#2015/06/19 Awake First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
while [ "$yn" != "yes" -a "$yn" != "YES" ] //(当condition条件成立时,就进行循环,直到condition的条件不成立才停止程序)$yn即不等于yes也不等于YES,那么就进入循环吧,如果等于就不进入循环
do //进入循环
read -p "Please input yes/YES to stop this program: " yn //循环的是什么呢?就是这条语句,提示用户输入变量内容,如果输入不正确就循环
done //如果输入正确的字符也就是condition的条件不成立了,那么停止程序
echo "OK! you input the correct answer."
上面这个例题说明的是当$yn这个变量不是yes且$yn也不是YES时,才进入循环内的程序,而如果$yn是yes或YES时,就会离开循环。
[root@awake scripts]# ./sh13.sh
Please input yes/YES to stop this program:yes //输入正确的参数
you input the correct answer.
[root@awake scripts]# ./sh13.sh
Please input yes/YES to stop this program:YES //输入正确的参数
you input the correct answer.
[root@awake scripts]# ./sh13.sh
Please input yes/YES to stop this program:yy
Please input yes/YES to stop this program:^C
上面的例子如果用until,它的条件会变成这样
脚本sh13-2.sh内容
[root@awake scripts]# more sh13-2.sh
#!/bin/bash
#Program
# Repeat question until user input correct answer.
#History:
#2015/06/19 Awake First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
until [ "$yn" == "yes" -o "$yn" == "YES" ] //(当conditiong条件成立时,就终止循环,否则就继续执行循环的程序段)当$yn变量内容等于yes或YES时,那么就终止循环,否则就循环
do
read -p "Please input yes/YES to stop this program:" yn
done
echo "OK! you input the correct answer."
[root@awake scripts]#
如果要计算1+2+3...+100这个数值,利用循环是这样的
脚本sh14.sh内容
#!/bin/bash
#program
# use loop to calculate "1+2+3...+100" result
#History:
#2015/06/19 Awake First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
s=0 //定义s的变量值为0(这是累加的数值变量)
i=0 //定义i的变量值为0(这是累计的数值,亦即是1,2,3....)
while [ "$i" != "100" ] //(当condition条件成立时,就进行下面的循环,直到condition的条件不成立才停止程序,此例到100就不成立了)也就是$i的数值会循环范围是1-99
do
i=$(($i+1)) //这个变量的定义感觉是覆盖了前面的i=0的变量,$i变量每次都会增加1,$i会从1变到99,也就是加99次,这个值会从1-100;
s=$(($s+$i)) //小括号重点的$s的值是0,并不会循环,这个值是0+1一次,然后是0+2一次,一直到100。
done
echo "The result of '1+2+3...+100' is == $s" 那么此处$s的结果就是5050了。
脚本sh14.sh的执行情况
[root@awake scripts]# ./sh14.sh
The result of '1+2+3...+100' is == 5050
[root@awake scripts]#
如果想要用户自行输入一个数字,让程序有1+2+...直到你输入数字为止,该如何编写?
脚本sh14-2.sh内容
#!/bin/bash
#program
# use loop to calculate "1+2+3...+100" result
#History:
#2015/06/19 Awake First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
s=0 //定义s的变量值为0(这是累加的数值变量)
i=0 //定义i的变量值为0(这是累计的数值,亦即是1,2,3....)
read -p "Use loop to calculate link 1+2+3... input you digit:" n
while [ "$i" != "$n" ] //(当condition条件成立时,就进行下面的循环,直到condition的条件不成立才停止程序,此例到100就不成立了)也就是$i的数值会循环范围是1-99
do
i=$(($i+1)) //这个变量的定义感觉是覆盖了前面的i=0的变量,$i变量每次都会增加1,$i会从1变到99,也就是加99次,这个值又加了1, 会从1-100;
s=$(($s+$i)) //小括号重点的$s的值是0,并不会循环,这个值是0+1一次,然后是0+2一次,一直到100。
done
echo "The result of '1+2+3...+100' is == $s" 那么此处$s的结果就是5050了。
shell脚本对变量类型没有限制,你输入数字,phone就可以直接当数值型来用。
只是这里的判断表达式中不支持直接正则匹配,你要用grep, sed, awk这些支持正则的工具才行,然后用 $? 取得执行状态来判断是否匹配成功。
read -p "Phone Number (xxxxxxxx):" phone
echo "$n" | egrep "^[0-9]{8}$" >/dev/null //查看用户输入的是否为8位数字,如果不是数字,那么将错误信息输入到null//
if [ $? -eq 0 ]; then //判断$?是否为0 如果为0那么向下执行
echo ....
我想做一个实例是限制用户的输入内容是数字,不可以是其他,不限制位数,怎么做?
for...do...done(固定循环)
相对于while,until的循环方式是必须要“符合某个条件”的状态,for这种语法则是“已经知道要进行几次循环”的状态,他的语法是:
for var in con1 con2 con3 ...
do
程序段
done
以上的例子来看,这个$var的变量内容在循环工作时:
1、第一次循环时,$var的内容为con1;
2、第二次循环时,$var的内容为con2;
3、第三次循环时,$var的内容为con3;
for...in...do...done案例1
假设我们有三种动物,分别是dog,cat,elephant三种,我想每一行都输出这样“There are dogs...”之类的字样。
脚本sh15.sh内容
#!/bin/bash
#Program:
# Using for ...loop to print 3 animals
#History
#2015/06/20 Awake 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
脚本sh15.sh执行结果如下
[root@RHEL6 scripts]# ./sh15.sh
There are dogs...
There are cats...
There are elephants...
[root@RHEL6 scripts]#
for...in...do...dones案例2
由于系统上面的各种账号都是写在/etc/passwd内的第一个字段,你能不能通过管道命令,cut找出单纯的账号名称后,以id和finger分别检查用户的标识符与特殊参数呢?由于不同的linux系统上面的账号都不一样,此时实际去获取/etc/passwd并使用循环处理就是一个可行的方案了.
脚本sh16.sh内容
cat > sh16.sh << "eof" //我要用cat直接输入的信息覆盖到sh16.sh中,且当键盘输入eof时,该次输入就结束
#!/bin/bash
#Program
# Use id,finger command to check system account's information
#History
#2015/06/23 Awake First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd) //依据-d的分割字符将一段信息切割成为数段,用-f取出第几段的意思,此例也就是取出每行的用户信息
for username in $users //username的变量内容为$users
do
id $username //用于显示用户的ID,以及所属群组的ID
finger $username //命令可以让使用者查询一些其他使用者的资料
done
eof
脚本sh16.sh执行情况如下
列出每一个账号的账号id和finger信息。
[root@RHEL6 scripts]# ./sh16.sh
uid=0(root) gid=0(root) groups=0(root)
Login: root Name: root
Directory: /root Shell: /bin/bash
On since Tue Jun 23 09:05 (CST) on pts/1 from 10.10.10.1
No mail.
No Plan.
uid=1(bin) gid=1(bin) groups=1(bin),2(daemon),3(sys)
Login: bin Name: bin
Directory: /bin Shell: /sbin/nologin
Never logged in.
No mail.
No Plan.
uid=2(daemon) gid=2(daemon) groups=2(daemon),1(bin),4(adm),7(lp)
Login: daemon Name: daemon
Directory: /sbin Shell: /sbin/nologin
Never logged in.
No mail.
No Plan.
uid=3(adm) gid=4(adm) groups=4(adm),3(sys)
Login: adm Name: adm
Directory: /var/adm Shell: /sbin/nologin
Never logged in.
No mail.
No Plan.
......
这个操作还可以用在每个账号的删除、更改上面!
for...in...do...dones案例3
脚本sh17.sh内容
cat > sh17.sh << "eof
#!/bin/bash
#Program
# Use ping command to check the network's PC state.
#History
#2015/06/23 Awake First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
netwrok="192.168.1"
for sitenu in $(seq 1 100) //seq用于产生从某个数到另外一个数之间的所有整数(包含起始数和结尾数)
do
ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1 //-c 表示ping的个数(这里为1个),-w表示指定等待每个响应的最长时间(这里为1秒);&>表示将正确与错误数据写入同一个文件,如果这些都执行正确那么将result的值赋予为0,否则赋予为1
if [ "result" == 0 ]; then
echo "Server ${network}.${sitenu} is UP."
else
echo "Server ${network}.${sitenu} id DOWN.
脚本sh17.sh执行情况如下
[root@RHEL6 scripts]# . sh17.sh
Server 192.168.1.1 is UP.
Server 192.168.1.2 is DOWN.
Server 192.168.1.3 is DOWN.
Server 192.168.1.4 is DOWN.
......
ping -w参数
[root@RHEL6 ~]# ping -w 1 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
--- 192.168.1.200 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 1000ms //指定1秒就超时
[root@RHEL6 ~]# ping -c 1 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
From 192.168.1.121 icmp_seq=1 Destination Host Unreachable
--- 192.168.1.200 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 3004ms //默认的超时时间3004ms
判断式加上固定循环实例
让用户输入某个目录文件名,然后找出某目录内的文件名的权限。利用这个脚本方式,还可以很轻易第处理一些文件的特性。
脚本sh18.sh内容
[root@RHEL6 scripts]# more sh18.sh
#!/bin/bash
#Program
# User input dir name, I find the permission of files.
#History
#2015/06/23 Awake First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
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
filelist=$(ls $dir) //列出所有在该目录下的文件名,包括目录名
for filename in $filelist //变量filename内容为$filelist,filelist的变量内容是ls $dir
do
perm="" //先赋予变量perm的内容为空,因为这个变量对于每个文件都要循环一次,因此先要将变量置为空,不置为空我想几个文件之后,接下来的文件都会有所有权限
test -r "$dir/$filename" && perm="$perm readable" //测试文件是否有只读属性,如果有就赋予perm的变量内容为“空和readable值?”
test -w "$dir/$filename" && perm="$perm writable" //我想知道的是这个perm="$perm writable"为什么没有覆盖上面的环境变量?这个的解释是这样,看下文吧
test -x "$dir/$filename" && perm="$perm executable"
echo "The file $dir/$filename's permission is $perm"
done
[root@RHEL6 scripts]#
perm="$perm xxx"的解读
[root@RHEL6 scripts]# perm=""
[root@RHEL6 scripts]# perm="$perm r" //此时$perm的内容为空,如果echo $perm,那么他的值应该是r
[root@RHEL6 scripts]# perm="$perm w" //此时$perm的内容已经是r,如果后面再跟一个w,那么echo $perm的值应该是r w
[root@RHEL6 scripts]# perm="$perm e" //此时$perm的内容已经是r w,如果后面再跟一个e,那么echo $perm的值应该是r w e
[root@RHEL6 scripts]# echo $perm
r w e
[root@RHEL6 scripts]#
脚本sh18.sh执行情况如下
[root@RHEL6 scripts]# ./sh18.sh
Please input a directory: /root/scripts
The file /root/scripts/sh01.sh's permission is readable writable
The file /root/scripts/sh02.sh's permission is readable writable
The file /root/scripts/sh03.sh's permission is readable writable
The file /root/scripts/sh04.sh's permission is readable writable
The file /root/scripts/sh05.sh's permission is readable writable executable
The file /root/scripts/sh06-2.sh's permission is readable writable executable
......
除了上述的方法外,for循环还有另外一种写法
for ((初始值; 限制值; 执行步长))
do
程序段
done
这种写法适合于数值方式的运算当中,在for后面的括号内的三串内容意义为:
初始值:某个变量在循环当中的初始值,直接以类似i=1设置好;
限制值:当变量的值在这个限制值得范围内,就连续进行循环,例如i<=100;
执行步长:没做一次循环时变量的变化量。脚本sh19.sh的内容如下
[root@RHEL6 scripts]# more sh19.sh
#!/bin/bash
#Program
# Try do calculate 1+2+...+$(your_input)
#History
#2015/06/23 Awake 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 //定义s的变量值为0
for ((i=1; i<=$nu; i=i+1)) //定义初始值i=1;限制值i<=$nu;执行步长i=i+1
do
s=$(($s+$i)) //小括号重点的$s的值是0,并不会循环,这个值是0+1一次,然后是0+2一次,一直到$nu
done
echo "The result of '1+2+3...+$nu' is == $s"
[root@RHEL6 scripts]#
脚本sh19.sh的执行情况
[root@RHEL6 scripts]# vi sh19.sh
[root@RHEL6 scripts]# ./sh19.sh
Please input a number, I will count for 1+2+...+your_input:100
The result of '1+2+3...+100' is == 5050
[root@RHEL6 scripts]# ./sh19.sh
Please input a number, I will count for 1+2+...+your_input:99
The result of '1+2+3...+99' is == 4950
[root@RHEL6 scripts]#