Linux基础之bash脚本进阶篇-循环语句(for,while,until)
20160909 补充break与continue的区别 什么是循环语句、死循环? 循环语句:将一段代码重复执行0、1或多次。 到底要重复运行多少次?以及我们如何设定循环语句的重复次数? 为了解决上面的问题于是就有了进入条件与退出条件。 进入条件:条件满足时进入循环。 退出条件:不符合条件退出循环。 一种特殊的循环:死循环 死循环:在编程中,一个无法靠自身的控制终止的循环称为"死循环"。死循环的出现有两种情况: 1、因程序需要刻意写的;2、因程序员的失误造成的。 第二种的死循环通常会造成比较严重的程序错误,甚至会因此而影响物理机。因此死循环的使用需要合理的设计。 实验环境CentOS7.2 本文重要的三个循环语句:for、while、until ……………………………………………………………………………………………………………………… for循环 for语句的使用格式: forNAME in LIST(列表);do 循环体 done 列表生成方式: (1)整数列表 {start..end} $(seq start [[step]end]) (2)glob /etc/rc.d/rc3.d/K* (3)命令 ……………………………………………………………………………………………………………………… 下面以一个例子看看for的具体作用 示例:计算1+2+...+10的值 1 2 3 4 5 6 7 8 9 #!/bin/bash #sumthevalueof"1+2+...+10" #authorchawan #date:20160906 declare -i sum =0 for x in {1..10}; do let sum +=$x done echo "Thesumis:$sum" 运行脚本0906-1结果如下 1 2 [root@dockerhmworks] #sh0906-1 The sum is:55 上面使用了第一种的整数列表中的第一种形式,这里如果是“1+2+...+n”这种形式那么{start..end}就不再适用,此时就只能使用$(seq start [[step]end])。下面再举一个例子说明 示例:计算“1+2+...+n”的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/bin/bash #sum“1+2+...+n” #authorchawan #date:20160906 declare -i sum =0 #以交互的方式输入一个正整数 read -p "Pleaseinsetanumber:" num #判断输入的数是否为空,为空则提示并退出 [-z$num]&& echo "Pleaseinputanumber!" && exit 1 #判断输入的是否是正整数,若是则执行循环,若不是提示输入正整数并退出 if [[$num=~^[1-9][0-9]{0,}$]]; then for i in {1..$num}; do #foriin`seq1$num`;do let sum +=$i #sum=$[$sum+$i]这种方式也可以不过不够简练 done else echo "Error:pleaseinputapositiveinteger" && exit 2 fi #显示最后的和 echo "Thesumis:$sum" 下面执行该脚本 1 2 3 4 [root@dockerhmworks] #sh0906-2 Pleaseinsetanumber:8 0906-2:行14: let : sum +={1..8}:语法错误:期待操作数(错误符号是 "{1..8}" ) The sum is:0 该结果说明{start..end}形式不适用于有变量出现的情况,既然这个不行就来试试$(seq start [[step]end]) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/bin/bash #sum“1+2+...+n” #authorchawan #date:20160906 declare -i sum =0 #以交互的方式输入一个正整数 read -p "Pleaseinsetanumber:" num #判断输入的数是否为空,为空则提示并退出 [-z$num]&& echo "Pleaseinputanumber!" && exit 1 #判断输入的是否是正整数,若是则执行循环,若不是提示输入正整数并退出 if [[$num=~^[1-9][0-9]{0,}$]]; then for i in ` seq 1$num`; do let sum +=$i #sum=$[$sum+$i] done else echo "Error:pleaseinputapositiveinteger" && exit 2 fi #显示最后的和 echo "Thesumis:$sum" 执行该脚本 1 2 3 4 5 6 [root@dockerhmworks] #sh0906-2 Pleaseinsetanumber:10 The sum is:55 [root@dockerhmworks] #sh0906-2 Pleaseinsetanumber:100 The sum is:5050 该结果表明$(seq start [[step]end])适用性更好,因此一般建议使用它。 列表的glob与命令这两种就不再具体演示。大家感兴趣可以自己尝试下做个实验体会体会。 ……………………………………………………………………………………………………………………… while循环 while语句使用格式: whileCONDITION;do 循环体 done CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环; 因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正; 进入条件:CONDITION为true; 退出条件:CONDITION为false ……………………………………………………………………………………………………………………… 示例:计算1+2+...+10的值 1 2 3 4 5 6 7 8 9 10 11 12 #!/bin/bash #sumthevalueof"1+2+...+10"while #authorchawan #date:20160906 #为了严谨起见,事先声明变量sum及i为整数型 declare -i sum =0 declare -ii=1 while [$i- le 10]; do let sum +=$i #sum=$[$sum+$i]的简写形式 let i++ #不断修正变量体 done echo "Thesumis:$sum" 执行脚本,查看其是否正确执行 1 2 [root@dockerhmworks] #sh0906-3 The sum is:55 while与for的不同在于: 1、不需要列表,因此可以大大节省内存空间,因为for如果列表很大会占用较多内容空间,对系统性能会造成影响,所以此时while的优越性就显现出来,它不需要占用很多内存空间,只需要两个变量的空间及做加法即可。 2、while需要修正体来不断修正变量,最终在符合退出条件时结束循环。 ……………………………………………………………………………………………………………………… until循环 until语句使用格式: untilCONDITION;do 循环体 done CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“false”,则执行一次循环;直到条件测试状态为“true”终止循环; 因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正; 进入条件:CONDITION为false; 退出条件:CONDITION为true until的用法同while,唯一的区别在于进入循环与退出循环的条件相反。 以相同的例子来体会二者的区别 ……………………………………………………………………………………………………………………… 示例:计算1+2+...+10的值 1 2 3 4 5 6 7 8 9 10 11 #!/bin/bash #sumthevalueof"1+2+...+10"until #authorchawan #date:20160906 declare -i sum =0 declare -ii=1 until [$i-gt10]; do let sum +=$i let i++ done echo "Thesumis:$sum" 执行脚本,查看结果是否正确输出 1 2 [root@dockerhmworks] #sh0906-4 The sum is:55 通过比较while与until的唯一差别就在于判断条件。这两者其实算是同一种循环语句,只是进入及退出循环的条件正好相反。 循环控制语句(用于循环体中) 1、continue[N]:提前结束第N层的本轮循环,而直接进入下一轮判断; 其使用格式: whileCONDTIITON1;do CMD1 ... ifCONDITION2;then continue fi CMDn ... done ……………………………………………………………………………………………………………………… 示例:求100以内所有偶数之和;要求循环遍历100以内的所正整数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #!/bin/bash #求100以内所有偶数之和;要求循环遍历100以内的所正整数 #authorchawan #date:20160906 declare -ii=1 declare -i sum =0 while [$i- le 100]; do let i++ #如果为奇数则跳过该循环 if [$[${i}%2]- eq 1]; then continue fi let sum +=$i done echo "Theevennumbersum:$sum" 执行脚本,查看结果是否正确显示 1 2 [root@dockerhmworks] #sh0906-5 Theevennumber sum :2550 之前我写这个脚本时是这么写的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #!/bin/bash #求100以内所有偶数之和;要求循环遍历100以内的所正整数 #authorchawan #date:20160906 declare -ii=1 declare -i sum =0 while [$i- le 100]; do let sum +=$i #如果为奇数则跳过该循环,直接进入下一轮判断后面的程序不再执行 if [$[${i}%2]- eq 1]; then continue fi let i++ done echo "Theevennumbersum:$sum" 这就是我个人由于对continue的理解不够准确而造成的死循环。 由于continue是跳过其所在循环,直接进入下一轮判断,后面的语句都不再执行。 当时没注意这点所以错误地把i++放在后面,这就导致若i起始值为奇数那么它就一直在重复执行。 这里只要将let sum+=$i与let i++调换为止即可正确执行。 ……………………………………………………………………………………………………………………… 2、break[N]:提前结束循环; 其使用格式: whileCONDTIITON1;do CMD1 ... ifCONDITION2;then break fi CMDn ... done break的使用通常是与死循环同时出现的,下面来介绍如何创建死循环 创建死循环: whiletrue;do 循环体 done untilfalse;do 循环体 done ……………………………………………………………………………………………………………………… 示例:每隔3秒钟到系统上获取已经登录的用户的信息;如果docker登录了,则记录于日志中,并退出; 1 2 3 4 5 6 7 8 9 10 11 12 #!/bin/bash #每隔3秒钟到系统上获取已经登录的用户的信息;如果docker用户登录,则记录于日志中,并退出脚本 #authorchawan #date:20160906 while true ; do if who | grep "^docker\>" $> /dev/null ; then break fi sleep 3 echo "dockerisnotlogin" done echo "dockerloggedon." >> /tmp/user .log 运行脚本 1 2 3 4 5 6 7 [root@dockerhmworks] #sh0906-6 dockerisnotlogin dockerisnotlogin dockerisnotlogin dockerisnotlogin dockerisnotlogin dockerisnotlogin 为了验证该脚本,下面我们使用docker用户登陆 docker用户登陆后查看/tmp/user.log文件 ######################################################################################### break与continue语句的区别: break语句和continue语句都可以位于各种循环体内,用于控制当前的循环流程。但,break语句是直接退出当前的循环结构,转向执行循环体后面的语句;而continue语句则只是跳过当前循环体中continue语句后面的语句,转向当前循环体的起始位置,重新执行下一次循环,并没有退出当前的循环结构。 这是两者最本质的区别:break跳出当前循环,continue没有跳出当前循环。 循环语句的特殊用法(while及for) while循环的特殊用法(遍历文件的每一行): 其使用格式: whileread line;do 循环体 done< /PATH/FROM/SOMEFILE 依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line ……………………………………………………………………………………………………………………… 示例:找出其ID号为偶数的所有用户,显示其用户名及ID号; 1 2 3 4 5 6 7 8 9 10 #!/bin/bash #找出其ID号为偶数的所有用户,显示其用户名及ID号 #authorchawan #date:20160906 while read line; do if [$[` echo $line| cut -d:-f3`%2]- eq 0]; then echo -e-n "username:`echo$line|cut-d:-f1`\t" echo "uid:`echo$line|cut-d:-f3`" fi done < /etc/passwd 运行脚本 ……………………………………………………………………………………………………………………… for循环的特殊格式: for((控制变量初始化;条件判断表达式;控制变量的修正表达式));do 循环体 done 控制变量初始化:仅在运行到循环代码段时执行一次; 条件判断表达式:在什么条件下进行循环; 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断; 示例:求100以内所正整数之和 1 2 3 4 5 6 7 8 9 #!/bin/bash #求100以内所正整数之和 #authorchawan #date:20160906 declare -i sum =0 for ((i=1;i<=100;i++)); do let sum +=$i done echo "Thesumis:$sum" 运行脚本,查看结果是否正确 1 2 [root@dockerhmworks] #sh0906-8 The sum is:5050 for的这种格式减少了代码量,看着更简洁,不过其限制是只适用于有数字出现的循环,若是对某目录下的所有文件进行某种循环的执行就不适应了。 循环嵌套 在本文的最后再以一题体会下循环嵌套的神奇 示例:打印九九乘法表 1 2 3 4 5 6 7 8 9 10 #!/bin/bash #打印九九乘法表 #authorchawan #date:20160906 for ((j=1;j<=9;j++)); do for ((i=1;i<=j;i++)) do echo -e-n "${i}X${j}=$[$i*$j]\t" done echo done 我在刚刚接触循环嵌套时各种晕,循环嵌套不是没有目的的乱用,而是根据自己的需求有目的的使用,比如要打印99乘法表,开始要分析99乘法表的规律,分析后我们发现它横行是连续的,因此要用到一个循环(通常在遇到连续的内容都会用到循环)它的列也是连续的,因此又用到一个循环,而99乘法表又是由两个变化的量构成,综上我们就可以确定,需要使用两个变量,这两个变量分别要用到循环,而一个变量又受到另一个变量的限制,因此这个受限的变量就是被嵌套的主。问题分析到这里,我们解决这个问题要用到的工具都找出来了:两个变量,每个变量对应一个循环,同时一个变量受到另一个变量的限制,也就是说它需要在其循环内进行嵌套。 下面就是靠自己去使用工具解决问题了。我相信大家这点应该都不成问题,问题就分析到这里。 小结: 本文主要介绍什么是循环,死循环,bash常用的三种循环语句for、while、until及循环控制语句continue、break 在本文结尾又介绍了while的特殊用法(遍历文件中的每一行),for的c语言格式。 至于什么时候用for什么时候用while需要自己在实际写脚本中细细比较,鉴于本人也是新手,这里就算想细说也只能望洋兴叹。 本文转自 紫色的茶碗 51CTO博客,原文链接:http://blog.51cto.com/chawan/1847024,如需转载请自行联系原作者