| Chapter 11. Loops and Branches | What needs this iteration, woman? --Shakespeare, Othello |
Operations on code blocks are the key to structured and organized shell scripts. Looping and branching constructs provide the tools foraccomplishing this.
11.1. LoopsA loop is a block of code thatiterates a list of commandsas long as the loop control conditionis true. for loops - for arg in [list]
This is the basic looping construct. It differs significantly from its C counterpart.
for arg in [list] do �command(s)... done | During each pass through the loop, arg takes on the value of each successive variable in the list. | for arg in "$var1" "$var2" "$var3" ... "$varN" # In pass 1 of the loop, arg = $var1 # In pass 2 of the loop, arg = $var2 # In pass 3 of the loop, arg = $var3 # ...# In pass N of the loop, arg = $varN# Arguments in [list] quoted to prevent possible word splitting. | The argument list may contain wild cards.
If do is on same line as for, there needs to be a semicolon after list. for arg in [list] ; do
Example 11-1. Simple for loops #!/bin/bash# Listing the planets.for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Plutodo echo $planet # Each planet on a separate line.doneecho; echofor planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto" # All planets on same line. # Entire 'list' enclosed in quotes creates a single variable. # Why? Whitespace incorporated into the variable.do echo $planetdoneecho; echo "Whoops! Pluto is no longer a planet!" 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 15-16) to force parsing of each [list] element and assignment of each component to the positional parameters. Example 11-2. for loop with two parameters in each [list] element #!/bin/bash# Planets revisited.# Associate the name of each planet with its distance from the sun.for planet in "Mercury 36" "Venus 67" "Earth 93" "Mars 142" "Jupiter 483" do set -- $planet # Parses variable "planet" #+ and sets positional parameters. # The "--" prevents nasty surprises if $planet is null or #+ begins with a dash. # May need to save original positional parameters, #+ since they get overwritten. # One way of doing this is to use an array, # original_params=("$@") echo "$1$2,000,000 miles from the sun" #-------two tabs---concatenate zeroes onto parameter $2done# (Thanks, S.C., for additional clarification.)exit 0 |
A variable may supply the [list] in a for loop. Example 11-3. Fileinfo: operating on a file list contained in a variable #!/bin/bash# fileinfo.shFILES="/usr/sbin/accept/usr/sbin/pwck/usr/sbin/chroot/usr/bin/fakefile/sbin/badblocks/sbin/ypbind" # List of files you are curious about. # Threw in a dummy file, /usr/bin/fakefile.echofor file in $FILESdo if [ ! -e "$file" ] # Check if file exists. then echo "$file does not exist."; echo continue # On to next. fi ls -l $file | awk '{ print $8 " file size: " $5 }' # Print 2 fields. whatis `basename $file` # File info. # Note that the whatis database needs to have been set up for this to work. # To do this, as root run /usr/bin/makewhatis. echodone exit 0 |
If the [list] in a for loop contains wild cards (* and ?) used in filename expansion, then globbing takes place. Example 11-4. Operating on files with a for loop #!/bin/bash# list-glob.sh: Generating [list] in a for-loop, using "globbing" ...# Globbing = filename expansion.echofor file in *# ^ Bash performs filename expansion#+ on expressions that globbing recognizes.do ls -l "$file" # Lists all files in $PWD (current directory). # Recall that the wild card character "*" matches every filename, #+ however, in "globbing," it doesn't match dot-files. # If the pattern matches no file, it is expanded to itself. # To prevent this, set the nullglob option #+ (shopt -s nullglob). # Thanks, S.C.doneecho; echofor file in [jx]*do rm -f $file # Removes only files beginning with "j" or "x" in $PWD. echo "Removed file "$file"".doneechoexit 0 |
Omitting the in [list] part of a for loop causes the loop to operate on $@ -- the positional parameters. A particularly clever illustration of this is Example A-15. See also Example 15-17. Example 11-5. Missing in [list] in afor loop #!/bin/bash# Invoke this script both with and without arguments,#+ and see what happens.for ado echo -n "$a " done# The 'in list' missing, therefore the loop operates on '$@'#+ (command-line argument list, including whitespace).echoexit 0 |
It is possible to use command substitution to generate the [list] in a for loop. See also Example 16-54, Example 11-10 and Example 16-48. Example 11-6. Generating the [list] in a for loop with command substitution #!/bin/bash# for-loopcmd.sh: for-loop with [list]#+ generated by command substitution.NUMBERS="9 7 3 8 37.53" for number in `echo $NUMBERS` # for number in 9 7 3 8 37.53do echo -n "$number " doneecho exit 0 | Here is a somewhat more complex example of using command substitution to create the [list]. Example 11-7. A grep replacement for binary files #!/bin/bash# bin-grep.sh: Locates matching strings in a binary file.# A "grep" replacement for binary files.# Similar effect to "grep -a" E_BADARGS=65E_NOFILE=66if [ $# -ne 2 ]then echo "Usage: `basename $0` search_string filename" exit $E_BADARGSfiif [ ! -f "$2" ]then echo "File "$2" does not exist." exit $E_NOFILEfi IFS=$' |
|
| |