bash provides two classes of two loop structure. The 1st of these is the "while" (or until) which is used when then number of iterations is unknown before hand, and the "for" loop which is used when the number of iterations is known or a list of items to process exists.
Like if, while invokes the argument following the "while" keyword as a command. As long as the return status of the command is true, the action body of the loop will execute. "until" performs the same except that the action body is executed as long as the argument command returns false.
The general structure of the while is :
while test-cmd do action-cmd ... done or while test-cmd; do action-cmd; done
where :
"while" (or until) initializes the loop.
"test-cmd" is the command to run and test for success. Like if, this may be a compound sequence of commands possibly containing pipes and redirection. The "while test-cmd" should be on its own line or followed by the semi-colon.
"do" marks the beginning of the action block.
action-cmd is the command to run if the while detects a true status for the test-cmd. The action block must contain at least one command or the : (colon) and may contain many commands including cases, ifs, other whiles, or for statements.
"done" terminates the action block.
The test-cmd is always run 1st, so it is possible that the commands in the action block do not run because of the starting condition.
bash (and Bourne) loop structures recognize the "break" and "continue" commands in the action body.
"break" causes the execution of the bash shell script to immediately continue after the "done" keyword. You may use "break" to prematurely exit the loop because of an error condition or another reason not caught by the test-cmd.
"continue", on the other hand, cause the execution of the loop to immediately return back to the "while" statement and re-run the test-command. If you use a "continue", make sure the conditions for breaking out of the loop can still occur.
guess () { # generate a random number between 1 and 1000 rand=$RANDOM rand=`expr $rand % 1001` # set initial over range guess=1001 count=0 # while guess not a match while test "$guess" != "$rand" do # Get command to run from user. echo -e "Pick a number between 0 and 1000 or q to quit \c" read guess # test for q to quit. if test "$guess" = q then break # if input not a number, go back and get new guess elif test -z "`echo $guess | grep '^[0-9]\{1,\}$' 2> /dev/null`" then echo "No, enter a number or q" continue # else count try and test guess against answer else count=`expr $count + 1` # give hints if test $guess -lt $rand then echo more elif test $guess -gt $rand then echo less elif test $guess -eq $rand then echo "Yes, you took $count tries." fi fi done }
Remember that bash will not run any commands in the loop body after the executed "continue" statement, so any actions required to modify the status of the the test-cmd following the "while" keyword must occur before the "continue" is acted on.
ans=y while [ "$ans" != n ] do if [ "$ans" = y ] then continue fi echo "continue y or n" read ans done
In the example above, the prompt and read will never be executed because of the position of the "continue"
In general, use the loop's primary test-cmd for the conditions that justify the use of the loop and use conditional breaks and continues to handle other situations that affect the execution of the loop.
When working with embedded loops, the break and continue apply only to the current loop level. To break (or continue) out of multiple layers of embedded loops, use an integer value after the break or continue. Value represents the number of levels to move.
# The following decrements a count by 1 or 2 depending on user choice. # Inner loop handles user input and -2 adjustment looper () { count=100; again=i # While the value to be decremented > 0 while [ $count -gt 0 ] do # Get user choice of adjustment and check for valid input while [ "$again" != q ] do echo -e "\n$count\n " echo "Choice : y - yes n - no r - repeat q - quit" echo -e "Deduct two? y, n, r, or q :" read again case "$again" in q ) break 2 ;; # break out of both loops and terminate r ) again=i; continue 2 ;; # go directly back to outer while n ) again=i; break ;; # break out of inner loop only y ) again=i;; # reset input and allow expr after case to run * ) echo "oops"; again=i; continue ;; # go directly to inner while esac count=`expr $count - 2` done count=`expr $count - 1` done }
The until works the same as while except that the loop body is run as long as the test-cmd returns false.
again="g" until test "$again" = q do actions echo "To quit, enter q" read again done
When the ending condition has to be determined by another command or user selected input, use "while" or "until".
When the iteration is based on known counts or a specific set of values, use the "for" command.