628 lines
10 KiB
HTML
628 lines
10 KiB
HTML
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||
|
<HTML
|
||
|
><HEAD
|
||
|
><TITLE
|
||
|
>Loop Control</TITLE
|
||
|
><META
|
||
|
NAME="GENERATOR"
|
||
|
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
|
||
|
REL="HOME"
|
||
|
TITLE="Advanced Bash-Scripting Guide"
|
||
|
HREF="index.html"><LINK
|
||
|
REL="UP"
|
||
|
TITLE="Loops and Branches"
|
||
|
HREF="loops.html"><LINK
|
||
|
REL="PREVIOUS"
|
||
|
TITLE="Nested Loops"
|
||
|
HREF="nestedloops.html"><LINK
|
||
|
REL="NEXT"
|
||
|
TITLE="Testing and Branching"
|
||
|
HREF="testbranch.html"></HEAD
|
||
|
><BODY
|
||
|
CLASS="SECT1"
|
||
|
BGCOLOR="#FFFFFF"
|
||
|
TEXT="#000000"
|
||
|
LINK="#0000FF"
|
||
|
VLINK="#840084"
|
||
|
ALINK="#0000FF"
|
||
|
><DIV
|
||
|
CLASS="NAVHEADER"
|
||
|
><TABLE
|
||
|
SUMMARY="Header navigation table"
|
||
|
WIDTH="100%"
|
||
|
BORDER="0"
|
||
|
CELLPADDING="0"
|
||
|
CELLSPACING="0"
|
||
|
><TR
|
||
|
><TH
|
||
|
COLSPAN="3"
|
||
|
ALIGN="center"
|
||
|
>Advanced Bash-Scripting Guide: </TH
|
||
|
></TR
|
||
|
><TR
|
||
|
><TD
|
||
|
WIDTH="10%"
|
||
|
ALIGN="left"
|
||
|
VALIGN="bottom"
|
||
|
><A
|
||
|
HREF="nestedloops.html"
|
||
|
ACCESSKEY="P"
|
||
|
>Prev</A
|
||
|
></TD
|
||
|
><TD
|
||
|
WIDTH="80%"
|
||
|
ALIGN="center"
|
||
|
VALIGN="bottom"
|
||
|
>Chapter 11. Loops and Branches</TD
|
||
|
><TD
|
||
|
WIDTH="10%"
|
||
|
ALIGN="right"
|
||
|
VALIGN="bottom"
|
||
|
><A
|
||
|
HREF="testbranch.html"
|
||
|
ACCESSKEY="N"
|
||
|
>Next</A
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
><HR
|
||
|
ALIGN="LEFT"
|
||
|
WIDTH="100%"></DIV
|
||
|
><DIV
|
||
|
CLASS="SECT1"
|
||
|
><H1
|
||
|
CLASS="SECT1"
|
||
|
><A
|
||
|
NAME="LOOPCONTROL"
|
||
|
></A
|
||
|
>11.3. Loop Control</H1
|
||
|
><TABLE
|
||
|
BORDER="0"
|
||
|
WIDTH="100%"
|
||
|
CELLSPACING="0"
|
||
|
CELLPADDING="0"
|
||
|
CLASS="EPIGRAPH"
|
||
|
><TR
|
||
|
><TD
|
||
|
WIDTH="45%"
|
||
|
> </TD
|
||
|
><TD
|
||
|
WIDTH="45%"
|
||
|
ALIGN="LEFT"
|
||
|
VALIGN="TOP"
|
||
|
><I
|
||
|
><P
|
||
|
><I
|
||
|
>Tournez cent tours, tournez mille tours,</I
|
||
|
></P
|
||
|
><P
|
||
|
><I
|
||
|
>Tournez souvent et tournez toujours . . .</I
|
||
|
></P
|
||
|
><P
|
||
|
><I
|
||
|
>--Verlaine, <SPAN
|
||
|
CLASS="QUOTE"
|
||
|
>"Chevaux de bois"</SPAN
|
||
|
></I
|
||
|
></P
|
||
|
></I
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
><P
|
||
|
></P
|
||
|
><DIV
|
||
|
CLASS="VARIABLELIST"
|
||
|
><P
|
||
|
><B
|
||
|
><A
|
||
|
NAME="BRKCONT1"
|
||
|
></A
|
||
|
>Commands affecting loop behavior</B
|
||
|
></P
|
||
|
><DL
|
||
|
><DT
|
||
|
><B
|
||
|
CLASS="COMMAND"
|
||
|
>break</B
|
||
|
>, <B
|
||
|
CLASS="COMMAND"
|
||
|
>continue</B
|
||
|
></DT
|
||
|
><DD
|
||
|
><P
|
||
|
>The <B
|
||
|
CLASS="COMMAND"
|
||
|
>break</B
|
||
|
> and <B
|
||
|
CLASS="COMMAND"
|
||
|
>continue</B
|
||
|
>
|
||
|
loop control commands
|
||
|
<A
|
||
|
NAME="AEN6981"
|
||
|
HREF="#FTN.AEN6981"
|
||
|
><SPAN
|
||
|
CLASS="footnote"
|
||
|
>[1]</SPAN
|
||
|
></A
|
||
|
>
|
||
|
correspond exactly to their counterparts in other
|
||
|
programming languages. The <B
|
||
|
CLASS="COMMAND"
|
||
|
>break</B
|
||
|
>
|
||
|
command terminates the loop (<EM
|
||
|
>breaks</EM
|
||
|
>
|
||
|
out of it), while <B
|
||
|
CLASS="COMMAND"
|
||
|
>continue</B
|
||
|
> causes a jump
|
||
|
to the next <A
|
||
|
HREF="loops1.html#ITERATIONREF"
|
||
|
>iteration</A
|
||
|
>
|
||
|
of the loop, skipping all the remaining commands in that
|
||
|
particular loop cycle.</P
|
||
|
><DIV
|
||
|
CLASS="EXAMPLE"
|
||
|
><A
|
||
|
NAME="EX28"
|
||
|
></A
|
||
|
><P
|
||
|
><B
|
||
|
>Example 11-21. Effects of <I
|
||
|
CLASS="FIRSTTERM"
|
||
|
>break</I
|
||
|
> and
|
||
|
<B
|
||
|
CLASS="COMMAND"
|
||
|
>continue</B
|
||
|
> in a loop</B
|
||
|
></P
|
||
|
><TABLE
|
||
|
BORDER="0"
|
||
|
BGCOLOR="#E0E0E0"
|
||
|
WIDTH="90%"
|
||
|
><TR
|
||
|
><TD
|
||
|
><FONT
|
||
|
COLOR="#000000"
|
||
|
><PRE
|
||
|
CLASS="PROGRAMLISTING"
|
||
|
>#!/bin/bash
|
||
|
|
||
|
LIMIT=19 # Upper limit
|
||
|
|
||
|
echo
|
||
|
echo "Printing Numbers 1 through 20 (but not 3 and 11)."
|
||
|
|
||
|
a=0
|
||
|
|
||
|
while [ $a -le "$LIMIT" ]
|
||
|
do
|
||
|
a=$(($a+1))
|
||
|
|
||
|
if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Excludes 3 and 11.
|
||
|
then
|
||
|
continue # Skip rest of this particular loop iteration.
|
||
|
fi
|
||
|
|
||
|
echo -n "$a " # This will not execute for 3 and 11.
|
||
|
done
|
||
|
|
||
|
# Exercise:
|
||
|
# Why does the loop print up to 20?
|
||
|
|
||
|
echo; echo
|
||
|
|
||
|
echo Printing Numbers 1 through 20, but something happens after 2.
|
||
|
|
||
|
##################################################################
|
||
|
|
||
|
# Same loop, but substituting 'break' for 'continue'.
|
||
|
|
||
|
a=0
|
||
|
|
||
|
while [ "$a" -le "$LIMIT" ]
|
||
|
do
|
||
|
a=$(($a+1))
|
||
|
|
||
|
if [ "$a" -gt 2 ]
|
||
|
then
|
||
|
break # Skip entire rest of loop.
|
||
|
fi
|
||
|
|
||
|
echo -n "$a "
|
||
|
done
|
||
|
|
||
|
echo; echo; echo
|
||
|
|
||
|
exit 0</PRE
|
||
|
></FONT
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
></DIV
|
||
|
><P
|
||
|
><A
|
||
|
NAME="BREAKPARAM"
|
||
|
></A
|
||
|
></P
|
||
|
><P
|
||
|
>The <B
|
||
|
CLASS="COMMAND"
|
||
|
>break</B
|
||
|
> command may optionally take a
|
||
|
parameter. A plain <B
|
||
|
CLASS="COMMAND"
|
||
|
>break</B
|
||
|
> terminates
|
||
|
only the innermost loop in which it is embedded,
|
||
|
but a <B
|
||
|
CLASS="COMMAND"
|
||
|
>break N</B
|
||
|
> breaks out of
|
||
|
<TT
|
||
|
CLASS="PARAMETER"
|
||
|
><I
|
||
|
>N</I
|
||
|
></TT
|
||
|
> levels of loop.</P
|
||
|
><DIV
|
||
|
CLASS="EXAMPLE"
|
||
|
><A
|
||
|
NAME="BREAKLEVELS"
|
||
|
></A
|
||
|
><P
|
||
|
><B
|
||
|
>Example 11-22. Breaking out of multiple loop levels</B
|
||
|
></P
|
||
|
><TABLE
|
||
|
BORDER="0"
|
||
|
BGCOLOR="#E0E0E0"
|
||
|
WIDTH="90%"
|
||
|
><TR
|
||
|
><TD
|
||
|
><FONT
|
||
|
COLOR="#000000"
|
||
|
><PRE
|
||
|
CLASS="PROGRAMLISTING"
|
||
|
>#!/bin/bash
|
||
|
# break-levels.sh: Breaking out of loops.
|
||
|
|
||
|
# "break N" breaks out of N level loops.
|
||
|
|
||
|
for outerloop in 1 2 3 4 5
|
||
|
do
|
||
|
echo -n "Group $outerloop: "
|
||
|
|
||
|
# --------------------------------------------------------
|
||
|
for innerloop in 1 2 3 4 5
|
||
|
do
|
||
|
echo -n "$innerloop "
|
||
|
|
||
|
if [ "$innerloop" -eq 3 ]
|
||
|
then
|
||
|
break # Try break 2 to see what happens.
|
||
|
# ("Breaks" out of both inner and outer loops.)
|
||
|
fi
|
||
|
done
|
||
|
# --------------------------------------------------------
|
||
|
|
||
|
echo
|
||
|
done
|
||
|
|
||
|
echo
|
||
|
|
||
|
exit 0</PRE
|
||
|
></FONT
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
></DIV
|
||
|
><P
|
||
|
>The <B
|
||
|
CLASS="COMMAND"
|
||
|
>continue</B
|
||
|
> command, similar to
|
||
|
<B
|
||
|
CLASS="COMMAND"
|
||
|
>break</B
|
||
|
>, optionally takes a parameter. A
|
||
|
plain <B
|
||
|
CLASS="COMMAND"
|
||
|
>continue</B
|
||
|
> cuts short the
|
||
|
current iteration within its loop and begins the next.
|
||
|
A <B
|
||
|
CLASS="COMMAND"
|
||
|
>continue N</B
|
||
|
> terminates all remaining
|
||
|
iterations at its loop level and continues with the
|
||
|
next iteration at the loop, <TT
|
||
|
CLASS="OPTION"
|
||
|
>N</TT
|
||
|
> levels
|
||
|
above.</P
|
||
|
><DIV
|
||
|
CLASS="EXAMPLE"
|
||
|
><A
|
||
|
NAME="CONTINUELEVELS"
|
||
|
></A
|
||
|
><P
|
||
|
><B
|
||
|
>Example 11-23. Continuing at a higher loop level</B
|
||
|
></P
|
||
|
><TABLE
|
||
|
BORDER="0"
|
||
|
BGCOLOR="#E0E0E0"
|
||
|
WIDTH="90%"
|
||
|
><TR
|
||
|
><TD
|
||
|
><FONT
|
||
|
COLOR="#000000"
|
||
|
><PRE
|
||
|
CLASS="PROGRAMLISTING"
|
||
|
>#!/bin/bash
|
||
|
# The "continue N" command, continuing at the Nth level loop.
|
||
|
|
||
|
for outer in I II III IV V # outer loop
|
||
|
do
|
||
|
echo; echo -n "Group $outer: "
|
||
|
|
||
|
# --------------------------------------------------------------------
|
||
|
for inner in 1 2 3 4 5 6 7 8 9 10 # inner loop
|
||
|
do
|
||
|
|
||
|
if [[ "$inner" -eq 7 && "$outer" = "III" ]]
|
||
|
then
|
||
|
continue 2 # Continue at loop on 2nd level, that is "outer loop".
|
||
|
# Replace above line with a simple "continue"
|
||
|
# to see normal loop behavior.
|
||
|
fi
|
||
|
|
||
|
echo -n "$inner " # 7 8 9 10 will not echo on "Group III."
|
||
|
done
|
||
|
# --------------------------------------------------------------------
|
||
|
|
||
|
done
|
||
|
|
||
|
echo; echo
|
||
|
|
||
|
# Exercise:
|
||
|
# Come up with a meaningful use for "continue N" in a script.
|
||
|
|
||
|
exit 0</PRE
|
||
|
></FONT
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
></DIV
|
||
|
><DIV
|
||
|
CLASS="EXAMPLE"
|
||
|
><A
|
||
|
NAME="CONTINUENEX"
|
||
|
></A
|
||
|
><P
|
||
|
><B
|
||
|
>Example 11-24. Using <I
|
||
|
CLASS="FIRSTTERM"
|
||
|
>continue N</I
|
||
|
> in an actual task</B
|
||
|
></P
|
||
|
><TABLE
|
||
|
BORDER="0"
|
||
|
BGCOLOR="#E0E0E0"
|
||
|
WIDTH="90%"
|
||
|
><TR
|
||
|
><TD
|
||
|
><FONT
|
||
|
COLOR="#000000"
|
||
|
><PRE
|
||
|
CLASS="PROGRAMLISTING"
|
||
|
># Albert Reiner gives an example of how to use "continue N":
|
||
|
# ---------------------------------------------------------
|
||
|
|
||
|
# Suppose I have a large number of jobs that need to be run, with
|
||
|
#+ any data that is to be treated in files of a given name pattern
|
||
|
#+ in a directory. There are several machines that access
|
||
|
#+ this directory, and I want to distribute the work over these
|
||
|
#+ different boxen.
|
||
|
# Then I usually nohup something like the following on every box:
|
||
|
|
||
|
while true
|
||
|
do
|
||
|
for n in .iso.*
|
||
|
do
|
||
|
[ "$n" = ".iso.opts" ] && continue
|
||
|
beta=${n#.iso.}
|
||
|
[ -r .Iso.$beta ] && continue
|
||
|
[ -r .lock.$beta ] && sleep 10 && continue
|
||
|
lockfile -r0 .lock.$beta || continue
|
||
|
echo -n "$beta: " `date`
|
||
|
run-isotherm $beta
|
||
|
date
|
||
|
ls -alF .Iso.$beta
|
||
|
[ -r .Iso.$beta ] && rm -f .lock.$beta
|
||
|
continue 2
|
||
|
done
|
||
|
break
|
||
|
done
|
||
|
|
||
|
exit 0
|
||
|
|
||
|
# The details, in particular the sleep N, are particular to my
|
||
|
#+ application, but the general pattern is:
|
||
|
|
||
|
while true
|
||
|
do
|
||
|
for job in {pattern}
|
||
|
do
|
||
|
{job already done or running} && continue
|
||
|
{mark job as running, do job, mark job as done}
|
||
|
continue 2
|
||
|
done
|
||
|
break # Or something like `sleep 600' to avoid termination.
|
||
|
done
|
||
|
|
||
|
# This way the script will stop only when there are no more jobs to do
|
||
|
#+ (including jobs that were added during runtime). Through the use
|
||
|
#+ of appropriate lockfiles it can be run on several machines
|
||
|
#+ concurrently without duplication of calculations [which run a couple
|
||
|
#+ of hours in my case, so I really want to avoid this]. Also, as search
|
||
|
#+ always starts again from the beginning, one can encode priorities in
|
||
|
#+ the file names. Of course, one could also do this without `continue 2',
|
||
|
#+ but then one would have to actually check whether or not some job
|
||
|
#+ was done (so that we should immediately look for the next job) or not
|
||
|
#+ (in which case we terminate or sleep for a long time before checking
|
||
|
#+ for a new job).</PRE
|
||
|
></FONT
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
></DIV
|
||
|
><DIV
|
||
|
CLASS="CAUTION"
|
||
|
><P
|
||
|
></P
|
||
|
><TABLE
|
||
|
CLASS="CAUTION"
|
||
|
WIDTH="90%"
|
||
|
BORDER="0"
|
||
|
><TR
|
||
|
><TD
|
||
|
WIDTH="25"
|
||
|
ALIGN="CENTER"
|
||
|
VALIGN="TOP"
|
||
|
><IMG
|
||
|
SRC="../images/caution.gif"
|
||
|
HSPACE="5"
|
||
|
ALT="Caution"></TD
|
||
|
><TD
|
||
|
ALIGN="LEFT"
|
||
|
VALIGN="TOP"
|
||
|
><P
|
||
|
>The <B
|
||
|
CLASS="COMMAND"
|
||
|
>continue N</B
|
||
|
> construct is
|
||
|
difficult to understand and tricky to use in any meaningful
|
||
|
context. It is probably best avoided.</P
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
></DIV
|
||
|
></DD
|
||
|
></DL
|
||
|
></DIV
|
||
|
></DIV
|
||
|
><H3
|
||
|
CLASS="FOOTNOTES"
|
||
|
>Notes</H3
|
||
|
><TABLE
|
||
|
BORDER="0"
|
||
|
CLASS="FOOTNOTES"
|
||
|
WIDTH="100%"
|
||
|
><TR
|
||
|
><TD
|
||
|
ALIGN="LEFT"
|
||
|
VALIGN="TOP"
|
||
|
WIDTH="5%"
|
||
|
><A
|
||
|
NAME="FTN.AEN6981"
|
||
|
HREF="loopcontrol.html#AEN6981"
|
||
|
><SPAN
|
||
|
CLASS="footnote"
|
||
|
>[1]</SPAN
|
||
|
></A
|
||
|
></TD
|
||
|
><TD
|
||
|
ALIGN="LEFT"
|
||
|
VALIGN="TOP"
|
||
|
WIDTH="95%"
|
||
|
><P
|
||
|
>These are shell <A
|
||
|
HREF="internal.html#BUILTINREF"
|
||
|
>builtins</A
|
||
|
>,
|
||
|
whereas other loop commands, such as <A
|
||
|
HREF="loops1.html#WHILELOOPREF"
|
||
|
>while</A
|
||
|
> and <A
|
||
|
HREF="testbranch.html#CASEESAC1"
|
||
|
>case</A
|
||
|
>, are <A
|
||
|
HREF="internal.html#KEYWORDREF"
|
||
|
>keywords</A
|
||
|
>.</P
|
||
|
></TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
><DIV
|
||
|
CLASS="NAVFOOTER"
|
||
|
><HR
|
||
|
ALIGN="LEFT"
|
||
|
WIDTH="100%"><TABLE
|
||
|
SUMMARY="Footer navigation table"
|
||
|
WIDTH="100%"
|
||
|
BORDER="0"
|
||
|
CELLPADDING="0"
|
||
|
CELLSPACING="0"
|
||
|
><TR
|
||
|
><TD
|
||
|
WIDTH="33%"
|
||
|
ALIGN="left"
|
||
|
VALIGN="top"
|
||
|
><A
|
||
|
HREF="nestedloops.html"
|
||
|
ACCESSKEY="P"
|
||
|
>Prev</A
|
||
|
></TD
|
||
|
><TD
|
||
|
WIDTH="34%"
|
||
|
ALIGN="center"
|
||
|
VALIGN="top"
|
||
|
><A
|
||
|
HREF="index.html"
|
||
|
ACCESSKEY="H"
|
||
|
>Home</A
|
||
|
></TD
|
||
|
><TD
|
||
|
WIDTH="33%"
|
||
|
ALIGN="right"
|
||
|
VALIGN="top"
|
||
|
><A
|
||
|
HREF="testbranch.html"
|
||
|
ACCESSKEY="N"
|
||
|
>Next</A
|
||
|
></TD
|
||
|
></TR
|
||
|
><TR
|
||
|
><TD
|
||
|
WIDTH="33%"
|
||
|
ALIGN="left"
|
||
|
VALIGN="top"
|
||
|
>Nested Loops</TD
|
||
|
><TD
|
||
|
WIDTH="34%"
|
||
|
ALIGN="center"
|
||
|
VALIGN="top"
|
||
|
><A
|
||
|
HREF="loops.html"
|
||
|
ACCESSKEY="U"
|
||
|
>Up</A
|
||
|
></TD
|
||
|
><TD
|
||
|
WIDTH="33%"
|
||
|
ALIGN="right"
|
||
|
VALIGN="top"
|
||
|
>Testing and Branching</TD
|
||
|
></TR
|
||
|
></TABLE
|
||
|
></DIV
|
||
|
></BODY
|
||
|
></HTML
|
||
|
>
|