Advanced Bash-Scripting HOWTO: A guide to shell scripting, using Bash | ||
---|---|---|
Prev | Chapter 3. Tutorial / Reference | Next |
A loop is a block of code that iterates (repeats) a list of commands as long as the loop control condition is true.
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 |
![]() | 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 |
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 |
![]() | 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 |
![]() | A while loop may have its stdin redirected to a file by a < at its end (see Example 3-73). |
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
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 |
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
![]() |
|
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 |
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 |