10.3. 循环控制
优质
小牛编辑
134浏览
2023-12-01
影响循环行为的命令
- break, continue
break和continue这两个循环控制命令
例子 10-20. break和continue命令在循环中的效果
1 #!/bin/bash 2 3 LIMIT=19 # 上限 4 5 echo 6 echo "Printing Numbers 1 through 20 (but not 3 and 11)." 7 8 a=0 9 10 while [ $a -le "$LIMIT" ] 11 do 12 a=$(($a+1)) 13 14 if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Excludes 3 and 11. 15 then 16 continue # 跳过本次循环剩下的语句. 17 fi 18 19 echo -n "$a " # 在$a等于3和11的时候,这句将不会执行. 20 done 21 22 # 练习: 23 # 为什么循环会打印到20? 24 25 echo; echo 26 27 echo Printing Numbers 1 through 20, but something happens after 2. 28 29 ################################################################## 30 31 # 同样的循环, 但是用'break'来代替'continue'. 32 33 a=0 34 35 while [ "$a" -le "$LIMIT" ] 36 do 37 a=$(($a+1)) 38 39 if [ "$a" -gt 2 ] 40 then 41 break # Skip entire rest of loop. 42 fi 43 44 echo -n "$a " 45 done 46 47 echo; echo; echo 48 49 exit 0
break命令可以带一个参数.一个不带参数的break循环只能退出最内层的循环,而break N可以退出N层循环.
例子 10-21. 多层循环的退出
1 #!/bin/bash 2 # break-levels.sh: 退出循环. 3 4 # "break N" 退出N层循环. 5 6 for outerloop in 1 2 3 4 5 7 do 8 echo -n "Group $outerloop: " 9 10 # -------------------------------------------------------- 11 for innerloop in 1 2 3 4 5 12 do 13 echo -n "$innerloop " 14 15 if [ "$innerloop" -eq 3 ] 16 then 17 break # 试试 break 2 来看看发生什么. 18 # (里面一层循环和外面一层循环都被退出了..) 19 fi 20 done 21 # -------------------------------------------------------- 22 23 echo 24 done 25 26 echo 27 28 exit 0
continue命令也可以像break带一个参数.一个不带参数的continue命令只去掉本次循环的剩余代码.而continue N将会把N层循环剩余的代码都去掉,但是循环的次数不变.
例子 10-22. 多层循环的continue
1 #!/bin/bash 2 # "continue N" 命令, 将让N层的循环全部被continue. 3 4 for outer in I II III IV V # 外部循环 5 do 6 echo; echo -n "Group $outer: " 7 8 # -------------------------------------------------------------------- 9 for inner in 1 2 3 4 5 6 7 8 9 10 # 内部循环 10 do 11 12 if [ "$inner" -eq 7 ] 13 then 14 continue 2 # continue 2层, 也就是到outer循环上. 15 # 将"continue 2"替换为一个单独的"continue" 16 # 来看一下一个正常循环的行为. 17 fi 18 19 echo -n "$inner " # 7 8 9 10 将不会被echo 20 done 21 # -------------------------------------------------------------------- 22 #译者注:如果在此处添加echo的话,当然也不会输出. 23 done 24 25 echo; echo 26 27 # 练习: 28 # 准备一个有意义的"continue N"的使用,放在脚本中. 29 30 exit 0
例子 10-23. 在实际的任务中使用"continue N"
1 # Albert Reiner 给出了一个关于使用"continue N"的例子: 2 # --------------------------------------------------------- 3 4 # 假定我有很多任务需要运行, 5 #+ 这些任务要处理一些数据,这些数据保存在一个目录下的文件里,文件是以预先给定的模式命名的 6 #+ 有几个机器会存取这个目录 7 #+ 我想把工作都分配给这几个不同的机器. 8 #+ 然后我一般会在每个机器里运行类似下面的代码: 9 10 while true 11 do 12 for n in .iso.* 13 do 14 [ "$n" = ".iso.opts" ] && continue 15 beta=${n#.iso.} 16 [ -r .Iso.$beta ] && continue 17 [ -r .lock.$beta ] && sleep 10 && continue 18 lockfile -r0 .lock.$beta || continue 19 echo -n "$beta: " `date` 20 run-isotherm $beta 21 date 22 ls -alF .Iso.$beta 23 [ -r .Iso.$beta ] && rm -f .lock.$beta 24 continue 2 25 done 26 break 27 done 28 29 # 在我的应用里的细节(尤其是sleep N)更一般的模式是: 30 # 31 32 while true 33 do 34 for job in {pattern} 35 do 36 {job already done or running} && continue 37 {mark job as running, do job, mark job as done} 38 continue 2 39 done 40 break # 而所谓的 `sleep 600' 只是想避免程序太快结束达不到演示的效果. 41 done 42 43 # 脚本只有当所有任务都完成之后才会停止运行 44 #+ (包括那些运行时新添加的任务). 45 #+ 46 #+ 通过使用合适的lockfiles可以使几个机器协作运作而不会产生重复的处理 47 #+ [在我的情况里,重复的处理会使处理时间延长多一倍时间,因此我很想避免这个问题]. 48 #+ 同样,如果每次都从头开始搜索,可以由文件名得到处理顺序 49 #+ 当然,还有一种办法也可以不使用`continue 2', 50 #+ 但这样就不得不检查相同的任务是不是已经完成过了 51 #+ (而我们应该立马来找到下一个要运行的任务) 52 #+ (在演示的情况里,检查新任务前我们终止或睡眠了一段长时间). 53 #
continue N结构如果被用在一个有意义的上下文中的话,往往都很难理解,并且技巧性很高.所以最好的方法就是尽量避免它.
注
这两个命令是shell的内建(builtins)命令,而不像其它的循环命令那样,比如while和case,这两个是关键字(keywords).