Chinaunix首页 | 论坛 | 博客
  • 博客访问: 308572
  • 博文数量: 84
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 890
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-13 16:46
文章分类

全部博文(84)

文章存档

2017年(2)

2016年(4)

2015年(78)

我的朋友

分类: LINUX

2015-06-23 20:02:46

shell script 循环(loop)
除了if...then...fi这种条件判断式之外,循环可能是程序当中最重要的一环了。循环可以不断执行某个程序段落,直到用户设置的条件达成为止,所以重点是那个“条件的达成”是什么。除了这种依据判断式达成与否的不定循环之外,还有另外一种已经固定要跑多少次的循环,可称为固定循环。

while do done和until do done(不定循环)

一般来说,不定循环最常见的就是下面这两种状态了。

  1. while [ condition ] //中括号内的条件就是判断式
  2. do //是循环的开始
  3. 程序字段 //循环的内容
  4. done //是循环的结束
while的中文是“当......时,在......期间”,所以,这种方式说的是当condition条件成立时,就进行循环,直到condition的条件不成立才停止的意思。

还有另外一种不定循环的方式
  1. until [ condition ]
  2. do
  3. 程序字段
  4. done
这种方式恰恰与while相反,他说的是当conditiong条件成立时,就终止循环,否则就继续执行循环的程序段。

我们以while来做一个简单的练习,假设我要让用户输入yes或者是YES才结束程序的执行,否则就一直进行告知用户输入合法的字符串。
脚本sh13.sh内容
  1. #!/bin/bash
  2. #Program
  3. #    Repeat question until user input correct answer.
  4. #History:
  5. #2015/06/19 Awake First release
  6. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  7. export PATH

  8. while [ "$yn" != "yes" -a "$yn" != "YES" ] //(当condition条件成立时,就进行循环,直到condition的条件不成立才停止程序)$yn即不等于yes也不等于YES,那么就进入循环吧,如果等于就不进入循环
  9. do //进入循环
  10. read -p "Please input yes/YES to stop this program: " yn //循环的是什么呢?就是这条语句,提示用户输入变量内容,如果输入不正确就循环
  11. done //如果输入正确的字符也就是condition的条件不成立了,那么停止程序
  12. echo "OK! you input the correct answer."
上面这个例题说明的是当$yn这个变量不是yes且$yn也不是YES时,才进入循环内的程序,而如果$yn是yes或YES时,就会离开循环。
  1. [root@awake scripts]# ./sh13.sh
  2. Please input yes/YES to stop this program:yes //输入正确的参数
  3.  you input the correct answer.
  4. [root@awake scripts]# ./sh13.sh
  5. Please input yes/YES to stop this program:YES //输入正确的参数
  6.  you input the correct answer.
  7. [root@awake scripts]# ./sh13.sh
  8. Please input yes/YES to stop this program:yy
  9. Please input yes/YES to stop this program:^C
上面的例子如果用until,它的条件会变成这样
脚本sh13-2.sh内容

  1. [root@awake scripts]# more sh13-2.sh
  2. #!/bin/bash
  3. #Program
  4. # Repeat question until user input correct answer.
  5. #History:
  6. #2015/06/19 Awake First release
  7. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  8. export PATH

  9. until [ "$yn" == "yes" -o "$yn" == "YES" ] //(当conditiong条件成立时,就终止循环,否则就继续执行循环的程序段)当$yn变量内容等于yes或YES时,那么就终止循环,否则就循环
  10. do
  11.         read -p "Please input yes/YES to stop this program:" yn
  12. done
  13. echo "OK! you input the correct answer."
  14. [root@awake scripts]#
如果要计算1+2+3...+100这个数值,利用循环是这样的
脚本sh14.sh内容

  1. #!/bin/bash
  2. #program
  3. # use loop to calculate "1+2+3...+100" result
  4. #History:
  5. #2015/06/19 Awake First release
  6. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  7. export PATH

  8. s=0 //定义s的变量值为0(这是累加的数值变量)
  9. i=0 //定义i的变量值为0(这是累计的数值,亦即是1,2,3....)
  10. while [ "$i" != "100" ] //(当condition条件成立时,就进行下面的循环,直到condition的条件不成立才停止程序,此例到100就不成立了)也就是$i的数值会循环范围是1-99
  11. do
  12. i=$(($i+1)) //这个变量的定义感觉是覆盖了前面的i=0的变量,$i变量每次都会增加1,$i会从1变到99,也就是加99次,这个值会从1-100;
  13. s=$(($s+$i)) //小括号重点的$s的值是0,并不会循环,这个值是0+1一次,然后是0+2一次,一直到100。
  14. done
  15. echo "The result of '1+2+3...+100' is == $s" 那么此处$s的结果就是5050了。
脚本sh14.sh的执行情况
  1. [root@awake scripts]# ./sh14.sh
  2. The result of '1+2+3...+100' is == 5050
  3. [root@awake scripts]#
如果想要用户自行输入一个数字,让程序有1+2+...直到你输入数字为止,该如何编写?
脚本sh14-2.sh内容

  1. #!/bin/bash
  2. #program
  3. # use loop to calculate "1+2+3...+100" result
  4. #History:
  5. #2015/06/19 Awake First release
  6. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  7. export PATH

  8. s=0 //定义s的变量值为0(这是累加的数值变量)
  9. i=0 //定义i的变量值为0(这是累计的数值,亦即是1,2,3....
  10. read -p "Use loop to calculate link 1+2+3... input you digit:" n
  11. while [ "$i" != "$n" ] //(当condition条件成立时,就进行下面的循环,直到condition的条件不成立才停止程序,此例到100就不成立了)也就是$i的数值会循环范围是1-99
  12. do
  13. i=$(($i+1)) //这个变量的定义感觉是覆盖了前面的i=0的变量,$i变量每次都会增加1,$i会从1变到99,也就是加99次,这个值又加了1, 会从1-100;
  14. s=$(($s+$i)) //小括号重点的$s的值是0,并不会循环,这个值是0+1一次,然后是0+2一次,一直到100。
  15. done
  16. echo "The result of '1+2+3...+100' is == $s" 那么此处$s的结果就是5050了。
shell脚本对变量类型没有限制,你输入数字,phone就可以直接当数值型来用。
只是这里的判断表达式中不支持直接正则匹配,你要用grep, sed, awk这些支持正则的工具才行,然后用 $? 取得执行状态来判断是否匹配成功。
  1. read -p "Phone Number (xxxxxxxx):" phone
  2. echo "$n" | egrep "^[0-9]{8}$" >/dev/null //查看用户输入的是否为8位数字,如果不是数字,那么将错误信息输入到null//
  3. if [ $? -eq 0 ]; then //判断$?是否为0 如果为0那么向下执行
  4.    echo ....
我想做一个实例是限制用户的输入内容是数字,不可以是其他,不限制位数,怎么做?

for...do...done(固定循环)
相对于while,until的循环方式是必须要“符合某个条件”的状态,for这种语法则是“已经知道要进行几次循环”的状态,他的语法是:
  1. for var in con1 con2 con3 ...
  2. do
  3. 程序段
  4. done
以上的例子来看,这个$var的变量内容在循环工作时:
1、第一次循环时,$var的内容为con1;
2、第二次循环时,$var的内容为con2;
3、第三次循环时,$var的内容为con3;

for...in...do...done案例1
假设我们有三种动物,分别是dog,cat,elephant三种,我想每一行都输出这样“There are dogs...”之类的字样。 
脚本sh15.sh内容

  1. #!/bin/bash
  2. #Program:
  3. #    Using for ...loop to print 3 animals
  4. #History
  5. #2015/06/20 Awake First release
  6. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  7. export PATH

  8. for animal in dog cat elephant
  9. do
  10. echo "There are ${animal}s..."
  11. done
脚本sh15.sh执行结果如下
  1. [root@RHEL6 scripts]# ./sh15.sh
  2. There are dogs...
  3. There are cats...
  4. There are elephants...
  5. [root@RHEL6 scripts]#
for...in...do...dones案例2
由于系统上面的各种账号都是写在/etc/passwd内的第一个字段,你能不能通过管道命令,cut找出单纯的账号名称后,以id和finger分别检查用户的标识符与特殊参数呢?由于不同的linux系统上面的账号都不一样,此时实际去获取/etc/passwd并使用循环处理就是一个可行的方案了.
脚本sh16.sh内容

  1. cat > sh16.sh << "eof" //我要用cat直接输入的信息覆盖到sh16.sh中,且当键盘输入eof时,该次输入就结束
  2. #!/bin/bash
  3. #Program
  4. #    Use id,finger command to check system account's information
  5. #History
  6. #2015/06/23 Awake First release
  7. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  8. export PATH

  9. users=$(cut -d ':' -f1 /etc/passwd) //依据-d的分割字符将一段信息切割成为数段,用-f取出第几段的意思,此例也就是取出每行的用户信息
  10. for username in $users //username的变量内容为$users
  11. do
  12. id $username      //用于显示用户的ID,以及所属群组的ID
  13. finger $username //命令可以让使用者查询一些其他使用者的资料
  14. done
  15. eof
脚本sh16.sh执行情况如下
列出每一个账号的账号id和finger信息。
  1. [root@RHEL6 scripts]# ./sh16.sh
  2. uid=0(root) gid=0(root) groups=0(root)
  3. Login: root                              Name: root
  4. Directory: /root                         Shell: /bin/bash
  5. On since Tue Jun 23 09:05 (CST) on pts/1 from 10.10.10.1
  6. No mail.
  7. No Plan.
  8. uid=1(bin) gid=1(bin) groups=1(bin),2(daemon),3(sys)
  9. Login: bin                               Name: bin
  10. Directory: /bin                          Shell: /sbin/nologin
  11. Never logged in.
  12. No mail.
  13. No Plan.
  14. uid=2(daemon) gid=2(daemon) groups=2(daemon),1(bin),4(adm),7(lp)
  15. Login: daemon                            Name: daemon
  16. Directory: /sbin                         Shell: /sbin/nologin
  17. Never logged in.
  18. No mail.
  19. No Plan.
  20. uid=3(adm) gid=4(adm) groups=4(adm),3(sys)
  21. Login: adm                              Name: adm
  22. Directory: /var/adm                     Shell: /sbin/nologin
  23. Never logged in.
  24. No mail.
  25. No Plan.
  26. ......
这个操作还可以用在每个账号的删除、更改上面!

for...in...do...dones案例3
脚本sh17.sh内容

  1. cat > sh17.sh << "eof
  2. #!/bin/bash
  3. #Program
  4. #    Use ping command to check the network's PC state.
  5. #History
  6. #2015/06/23 Awake First release
  7. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  8. export PATH
  9. netwrok="192.168.1"
  10. for sitenu in $(seq 1 100) //seq用于产生从某个数到另外一个数之间的所有整数(包含起始数和结尾数)
  11. do
  12. ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1 //-c 表示ping的个数(这里为1个),-w表示指定等待每个响应的最长时间(这里为1秒);&>表示将正确与错误数据写入同一个文件,如果这些都执行正确那么将result的值赋予为0,否则赋予为1
  13. if [ "result" == 0 ]; then
  14. echo "Server ${network}.${sitenu} is UP."
  15. else
  16. echo "Server ${network}.${sitenu} id DOWN.
脚本sh17.sh执行情况如下
  1. [root@RHEL6 scripts]# . sh17.sh
  2. Server 192.168.1.1 is UP.
  3. Server 192.168.1.2 is DOWN.
  4. Server 192.168.1.3 is DOWN.
  5. Server 192.168.1.4 is DOWN.
  6. ......
ping -w参数 
  1. [root@RHEL6 ~]# ping -w 1 192.168.1.200
  2. PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.

  3. --- 192.168.1.200 ping statistics ---
  4. 1 packets transmitted, 0 received, 100% packet loss, time 1000ms //指定1秒就超时

  5. [root@RHEL6 ~]# ping -c 1 192.168.1.200
  6. PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
  7. From 192.168.1.121 icmp_seq=1 Destination Host Unreachable

  8. --- 192.168.1.200 ping statistics ---
  9. 1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 3004ms //默认的超时时间3004ms
判断式加上固定循环实例
让用户输入某个目录文件名,然后找出某目录内的文件名的权限。利用这个脚本方式,还可以很轻易第处理一些文件的特性。
脚本sh18.sh内容
  1. [root@RHEL6 scripts]# more sh18.sh
  2. #!/bin/bash
  3. #Program
  4. # User input dir name, I find the permission of files.
  5. #History
  6. #2015/06/23 Awake First release
  7. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  8. export PATH

  9. read -p "Please input a directory: " dir
  10. if [ "$dir" == "" -o ! -d "$dir" ]; then //判断用户输入的变量内容是否为空,或者不是目录,如果为空或者不是目录那么执行下面的语句,就是判断用户输入的是否为目录
  11.         echo "The $dir is NOT exist in your system."
  12.         exit 1
  13. fi
  14. filelist=$(ls $dir) //列出所有在该目录下的文件名,包括目录名
  15. for filename in $filelist //变量filename内容为$filelist,filelist的变量内容是ls $dir
  16. do
  17.         perm="" //先赋予变量perm的内容为空,因为这个变量对于每个文件都要循环一次,因此先要将变量置为空,不置为空我想几个文件之后,接下来的文件都会有所有权限
  18.         test -r "$dir/$filename" && perm="$perm readable" //测试文件是否有只读属性,如果有就赋予perm的变量内容为“空和readable值?”
  19.         test -w "$dir/$filename" && perm="$perm writable" //我想知道的是这个perm="$perm writable"为什么没有覆盖上面的环境变量?这个的解释是这样,看下文吧
  20.         test -x "$dir/$filename" && perm="$perm executable"
  21.         echo "The file $dir/$filename's permission is $perm"
  22. done
  23. [root@RHEL6 scripts]#
perm="$perm xxx"的解读
  1. [root@RHEL6 scripts]# perm=""
  2. [root@RHEL6 scripts]# perm="$perm r"  //此时$perm的内容为空,如果echo $perm,那么他的值应该是r
  3. [root@RHEL6 scripts]# perm="$perm w"  //此时$perm的内容已经是r,如果后面再跟一个w,那么echo $perm的值应该是r w
  4. [root@RHEL6 scripts]# perm="$perm e"  //此时$perm的内容已经是r w,如果后面再跟一个e,那么echo $perm的值应该是r w e
  5. [root@RHEL6 scripts]# echo $perm
  6. r w e
  7. [root@RHEL6 scripts]#
脚本sh18.sh执行情况如下
  1. [root@RHEL6 scripts]# ./sh18.sh
  2. Please input a directory: /root/scripts
  3. The file /root/scripts/sh01.sh's permission is readable writable
  4. The file /root/scripts/sh02.sh's permission is readable writable
  5. The file /root/scripts/sh03.sh's permission is readable writable
  6. The file /root/scripts/sh04.sh's permission is readable writable
  7. The file /root/scripts/sh05.sh's permission is readable writable executable
  8. The file /root/scripts/sh06-2.sh's permission is readable writable executable
  9. ......
除了上述的方法外,for循环还有另外一种写法
  1. for ((初始值; 限制值; 执行步长))
  2. do
  3. 程序段
  4. done
这种写法适合于数值方式的运算当中,在for后面的括号内的三串内容意义为:
初始值:某个变量在循环当中的初始值,直接以类似i=1设置好;
限制值:当变量的值在这个限制值得范围内,就连续进行循环,例如i<=100;
执行步长:没做一次循环时变量的变化量。 
脚本sh19.sh的内容如下

  1. [root@RHEL6 scripts]# more sh19.sh
  2. #!/bin/bash
  3. #Program
  4. # Try do calculate 1+2+...+$(your_input)
  5. #History
  6. #2015/06/23 Awake First release
  7. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
  8. export PATH

  9. read -p "Please input a number, I will count for 1+2+...+your_input:" nu  //让用户输入数字,这一部分没有判断,用户可以输入任意字符,只是脚本执行不下去,我还没有写好如何限定用户输入的一定是数字才行。

  10. s=0   //定义s的变量值为0
  11. for ((i=1; i<=$nu; i=i+1))  //定义初始值i=1;限制值i<=$nu;执行步长i=i+1
  12. do
  13.         s=$(($s+$i))    //小括号重点的$s的值是0,并不会循环,这个值是0+1一次,然后是0+2一次,一直到$nu
  14. done
  15. echo "The result of '1+2+3...+$nu' is == $s"
  16. [root@RHEL6 scripts]#
脚本sh19.sh的执行情况
  1. [root@RHEL6 scripts]# vi sh19.sh
  2. [root@RHEL6 scripts]# ./sh19.sh
  3. Please input a number, I will count for 1+2+...+your_input:100
  4. The result of '1+2+3...+100' is == 5050
  5. [root@RHEL6 scripts]# ./sh19.sh
  6. Please input a number, I will count for 1+2+...+your_input:99
  7. The result of '1+2+3...+99' is == 4950
  8. [root@RHEL6 scripts]#




阅读(2487) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~