3.8. Loops

A loop is a block of code that iterates (repeats) a list of commands as long as the loop control condition is true.

for (in)

This is the basic looping construct. It differs significantly from its C counterpart.

for [arg] in [list]
do
 command...
done

Note that list may contain wild cards.

Note further that if do is on same line as for, there needs to be a semicolon before list.

for [arg] in [list] ; do


Example 3-26. Simple for loops

   1 #!/bin/bash
   2 
   3 for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto
   4 do
   5   echo $planet
   6 done
   7 
   8 echo
   9 
  10 # Entire 'list' enclosed in quotes creates a single variable.
  11 for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto"
  12 do
  13   echo $planet
  14 done
  15 
  16 exit 0

Note

Each [list] element may contain multiple parameters. This is useful when processing parameters in groups. In such cases, use the set command (see Example 3-40) to force parsing of each [list] element and assignment of each component to the positional parameters.


Example 3-27. for loop with two parameters in each [list] element

   1 #!/bin/bash
   2 # Planets revisited.
   3 
   4 # Want to associate name of each planet with its distance from the sun.
   5 
   6 for planet in "Mercury 36" "Venus 67" "Earth 93"  "Mars 142" "Jupiter 483"
   7 do
   8   set $planet  # Parses variable "planet" and sets positional parameters.
   9   # May need to save original positional parameters, since they get overwritten.
  10   echo "$1		$2,000,000 miles from the sun"
  11   #-------two  tabs---concatenate zeroes onto parameter $2
  12 done
  13 
  14 exit 0

Omitting the in [list] part of a for loop causes the loop to operate on $*, the list of arguments given on the command line to the script.


Example 3-28. Missing in [list] in a for loop

   1 #!/bin/bash
   2 
   3 # Invoke both with and without arguments,
   4 # and see what happens.
   5 
   6 for a
   7 do
   8  echo $a
   9 done
  10 
  11 # 'in list' missing, therefore operates on '$*'
  12 # (command-line argument list)
  13 
  14 exit 0


Example 3-29. Using efax in batch mode

   1 #!/bin/bash
   2 
   3 if [ $# -ne 2 ]
   4 # Check for proper no. of command line args.
   5 then
   6    echo "Usage: `basename $0` phone# text-file"
   7    exit 1
   8 fi
   9 
  10 
  11 if [ ! -f $2 ]
  12 then
  13   echo "File $2 is not a text file"
  14   exit 2
  15 fi
  16   
  17 
  18 # Create fax formatted files from text files.
  19 fax make $2
  20 
  21 for file in $(ls $2.0*)
  22 # Concatenate the converted files.
  23 # Uses wild card in variable list.
  24 do
  25   fil="$fil $file"
  26 done  
  27 
  28 # Do the work.
  29 efax -d /dev/ttyS3 -o1 -t "T$1" $fil
  30 
  31 exit 0

while

This construct tests for a condition at the top of a loop, and keeps looping as long as that condition is true.

while [condition]
do
 command...
done

As is the case with for/in loops, placing the do on the same line as the condition test requires a semicolon.

while [condition] ; do

Note that certain specialized while loops, as, for example, a getopts construct, deviate somewhat from the standard template given here (see Section 3.9).


Example 3-30. Simple while loop

   1 #!/bin/bash
   2 
   3 var0=0
   4 
   5 while [ "$var0" -lt 10 ]
   6 do
   7   echo -n "$var0 "
   8   # -n suppresses newline.
   9   var0=`expr $var0 + 1`
  10   # var0=$(($var0+1)) also works.
  11 done
  12 
  13 echo
  14 
  15 exit 0


Example 3-31. Another while loop

   1 #!/bin/bash
   2 
   3 echo
   4 
   5 while [ "$var1" != end ]
   6 do
   7   echo "Input variable #1 (end to exit) "
   8   read var1
   9   # It's not 'read $var1' because value of var1 is being set.
  10   echo "variable #1 = $var1"
  11   # Need quotes because of #
  12   echo
  13 done  
  14 
  15 # Note: Echoes 'end' because termination condition tested for at top of loop.
  16 
  17 exit 0

Note

A while loop may have multiple conditions. Only the final condition determines when the loop terminates. This necessitates a slightly different loop syntax, however.


Example 3-32. while loop with multiple conditions

   1 #!/bin/bash
   2 
   3 var1=unset
   4 previous=$var1
   5 
   6 while echo "previous-variable = $previous"
   7       echo
   8       previous=$var1
   9       [ "$var1" != end ] # Keeps track of what "var1" was previously.
  10       # Four conditions on "while", but only last one controls loop.
  11       # Controlling condition has [ test ] brackets.
  12 do
  13 echo "Input variable #1 (end to exit) "
  14   read var1
  15   echo "variable #1 = $var1"
  16 done  
  17 
  18 # Try to figure out how this all works.
  19 # It's a wee bit tricky.
  20 
  21 exit 0

Note

A while loop may have its stdin redirected to a file by a < at its end (see Example 3-73).

until

This construct tests for a condition at the top of a loop, and keeps looping as long as that condition is false (opposite of while loop).

until [condition-is-true]
do
 command...
done

Note that an until loop tests for the terminating condition at the top of the loop, differing from a similar construct in some programming languages.

As is the case with for/in loops, placing the do on the same line as the condition test requires a semicolon.

until [condition-is-true] ; do


Example 3-33. until loop

   1 #!/bin/bash
   2 
   3 until [ "$var1" = end ]
   4 # Tests condition at top of loop.
   5 do
   6   echo "Input variable #1 "
   7   echo "(end to exit)"
   8   read var1
   9   echo "variable #1 = $var1"
  10 done  
  11 
  12 exit 0

break, continue

The break and continue loop control commands correspond exactly to their counterparts in other programming languages. The break command terminates the loop (breaks out of it), while continue causes a jump to the next iteration of the loop, skipping all the remaining commands in that particular loop cycle.


Example 3-34. Effects of break and continue in a loop

   1 #!/bin/bash
   2 
   3 echo
   4 echo Printing Numbers 1 through 20.
   5 
   6 a=0
   7 
   8 while [ $a -le 19 ]
   9 
  10 do
  11  a=$(($a+1))
  12 
  13  if [ $a -eq 3 ] || [ $a -eq 11 ]
  14  # Excludes 3 and 11
  15  then
  16    continue
  17    # Skip rest of this particular loop iteration.
  18  fi
  19 
  20  echo -n "$a "
  21 done 
  22 
  23 # Exercise for reader:
  24 # Why does loop print up to 20?
  25 
  26 echo
  27 echo
  28 
  29 echo Printing Numbers 1 through 20, but something happens after 2.
  30 
  31 ##################################################################
  32 
  33 # Same loop, but substituting 'break' for 'continue'.
  34 
  35 a=0
  36 
  37 while [ $a -le 19 ]
  38 do
  39  a=$(($a+1))
  40 
  41  if [ $a -gt 2 ]
  42  then
  43    break
  44    # Skip entire rest of loop.
  45  fi
  46 
  47  echo -n "$a "
  48 done
  49 
  50 echo
  51 echo
  52 
  53 exit 0

case (in) / esac

The case construct is the shell equivalent of switch in C/C++. It permits branching to one of a number of code blocks, depending on condition tests. It serves as a kind of shorthand for multiple if/then/else statements and is an appropriate tool for creating menus.

case "$variable" in

 "$condition1" )
 command...
 ;;

 "$condition2" )
 command...
 ;;

esac

Note

  • Quoting the variables is recommended.

  • Each test line ends with a left paren ).

  • Each condition block ends with a double semicolon ;;.

  • The entire case block terminates with an esac (case spelled backwards).


Example 3-35. Using case

   1 #!/bin/bash
   2 
   3 echo
   4 echo "Hit a key, then hit return."
   5 read Keypress
   6 
   7 case "$Keypress" in
   8   [a-z]   ) echo "Lowercase letter";;
   9   [A-Z]   ) echo "Uppercase letter";;
  10   [0-9]   ) echo "Digit";;
  11   *       ) echo "Punctuation, whitespace, or other";;
  12 esac
  13 # Allows ranges of characters in [square brackets].
  14 
  15 exit 0


Example 3-36. Creating menus using case

   1 #!/bin/bash
   2 
   3 # Crude rolodex-type database
   4 
   5 clear
   6 # Clear the screen.
   7 
   8 echo "          Contact List"
   9 echo "          ------- ----"
  10 echo "Choose one of the following persons:" 
  11 echo
  12 echo "[E]vans, Roland"
  13 echo "[J]ones, Mildred"
  14 echo "[S]mith, Julie"
  15 echo "[Z]ane, Morris"
  16 echo
  17 
  18 read person
  19 
  20 case "$person" in
  21 # Note variable is quoted.
  22 
  23   "E" | "e" )
  24   # Accept upper or lowercase input.
  25   echo
  26   echo "Roland Evans"
  27   echo "4321 Floppy Dr."
  28   echo "Hardscrabble, CO 80753"
  29   echo "(303) 734-9874"
  30   echo "(303) 734-9892 fax"
  31   echo "revans@zzy.net"
  32   echo "Business partner & old friend"
  33   ;;
  34 # Note double semicolon to terminate
  35 # each option.
  36 
  37   "J" | "j" )
  38   echo
  39   echo "Mildred Jones"
  40   echo "249 E. 7th St., Apt. 19"
  41   echo "New York, NY 10009"
  42   echo "(212) 533-2814"
  43   echo "(212) 533-9972 fax"
  44   echo "milliej@loisaida.com"
  45   echo "Girlfriend"
  46   echo "Birthday: Feb. 11"
  47   ;;
  48 
  49 # Add info for Smith & Zane later.
  50 
  51           * )
  52    # Default option.	  
  53    echo
  54    echo "Not yet in database."
  55   ;;   
  56 
  57 
  58 esac
  59 
  60 echo
  61 
  62 exit 0

select

The select construct, adopted from the Korn Shell, is yet another tool for building menus.

select variable [in list]
do
 command...
 break
done

This prompts the user to enter one of the choices presented in the variable list. Note that select uses the PS3 prompt (#? ) by default, but that this may be changed.


Example 3-37. Creating menus using select

   1 #!/bin/bash
   2 
   3 PS3='Choose your favorite vegetable: '
   4 # Sets the prompt string.
   5 
   6 echo
   7 
   8 select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"
   9 do
  10   echo
  11   echo "Your favorite veggie is $vegetable."
  12   echo "Yuck!"
  13   echo
  14   break
  15   # if no 'break' here, keeps looping forever.
  16 done
  17 
  18 exit 0

If in list is omitted, then select uses the list of command line arguments ($@) passed to the script or to the function in which the select construct is embedded. (Compare this to the behavior of a

for variable [in list]

construct with the in list omitted.)


Example 3-38. Creating menus using select in a function

   1 #!/bin/bash
   2 
   3 PS3='Choose your favorite vegetable: '
   4 
   5 echo
   6 
   7 choice_of()
   8 {
   9 select vegetable
  10 # [in list] omitted, so 'select' uses arguments passed to function.
  11 do
  12   echo
  13   echo "Your favorite veggie is $vegetable."
  14   echo "Yuck!"
  15   echo
  16   break
  17 done
  18 }
  19 
  20 choice_of beans rice carrots radishes tomatoes spinach
  21 #         $1    $2   $3      $4       $5       $6
  22 #         passed to choice_of() function
  23 
  24 exit 0