10.1. 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 loops

for (in)

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

for arg in [list]
do
 command(s)...
done

Note

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

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 11-12) to force parsing of each [list] element and assignment of each component to the positional parameters.

A variable may supply the [list] in a for loop.

The [list] in a for loop may contain filename globbing, that is, using wildcards for filename expansion.

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. A particularly clever illustration of this is Example A-17.

It is possible to use command substitution to generate the [list] in a for loop. See also Example 12-39, Example 10-10 and Example 12-33.

This is a somewhat more complex example of using command substitution to create the [list].

Example 10-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=65
E_NOFILE=66

if [ $# -ne 2 ]
then
  echo "Usage: `basename $0` string filename"
  exit $E_BADARGS
fi

if [ ! -f "$2" ]
then
  echo "File \"$2\" does not exist."
  exit $E_NOFILE
fi  


for word in $( strings "$2" | grep "$1" )
# The "strings" command lists strings in binary files.
# Output then piped to "grep", which tests for desired string.
do
  echo $word
done

# As S.C. points out, the above for-loop could be replaced with the simpler
#    strings "$2" | grep "$1" | tr -s "$IFS" '[\n*]'


# Try something like  "./bin-grep.sh mem /bin/ls"  to exercise this script.

exit 0

More of the same.

A final example of the [list] resulting from command substitution.

The output of a for loop may be piped to a command or commands.

The stdout of a loop may be redirected to a file, as this slight modification to the previous example shows.

There is an alternative syntax to a for loop that will look very familiar to C programmers. This requires double parentheses.

See also Example 26-8, Example 26-9, and Example A-7.

---

Now, a for-loop used in a "real-life" context.

while

This construct tests for a condition at the top of a loop, and keeps looping as long as that condition is true (returns a 0 exit status). In contrast to a for loop, a while loop finds use in situations where the number of loop repetitions is not known beforehand.

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.

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

As with a for loop, a while loop may employ C-like syntax by using the double parentheses construct (see also Example 9-26).

Note

A while loop may have its stdin redirected to a file by a < at its end.

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