10.3. 循环控制

影响循环行为的命令

break, continue

breakcontinue这两个循环控制命令[1]与其它语言的类似命令的行为是相同的. 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 #

Caution

continue N结构如果被用在一个有意义的上下文中的话,往往都很难理解,并且技巧性很高.所以最好的方法就是尽量避免它.

[1]

这两个命令是shell的内建(builtins)命令,而不像其它的循环命令那样,比如whilecase,这两个是关键字(keywords).