Looping for a While

Problem

You want your shell script to perform some actions repeatedly as long as some condition is met.

Solution

Use the while looping construct for arithmetic conditions:

while (( COUNT < MAX ))
do
    some stuff
    let COUNT++
done

for filesystem-related conditions:

while [ -z "$LOCKFILE" ]
do
    some things
done

or for reading input:

while read lineoftext
do
    process $lineoftext
done

Discussion

The double parentheses in our first while statement are just arithmetic expressions, very much like the $(( )) expression for shell variable assignment. They bound an arithmetic expression and assume that variable names mentioned inside the parentheses are meant to be dereferenced. That is, you don’t write $VAR, and instead use VAR inside the parentheses.

The use of the square brackets in while[ -z"$LOCKFILE" ] is the same as with the if statement—the single square bracket is the same as using the test statement.

The last example, while read lineoftext, doesn’t have any parentheses, brackets, or braces. The syntax of the while statement in bash is defined such that the condition of the while statement is a list of statements to be executed (just like the if statement), and the exit status of the last one determines whether the condition is true or false. An exit status of zero, and the condition is considered true, otherwise false.

A read statement returns a 0 on a successful read and a -1 on end-of-file, which means that the while will find it true for any successful read, but when the end of file is reached (and -1 returned) the while condition will be false and the looping will end. At that point, the next statement to be executed will be the statement after the done statement.

This logic of “keep looping while the statement returns zero” might seem a bit flipped—most C-like languages use the opposite, namely, “loop while nonzero.” But in the shell, a zero return value means everything went well; non-zero return values indicate an error exit.

This explains what happens with the (()) construct, too. Any expression inside the parentheses is evaluated, and if the result is nonzero, then the result of the (()) is to return a zero; similarly, a zero result returns a one. This means we can write expressions like Java or C programmers would, but the while statement still works as always in bash, expecting a zero result to be true.

In practical terms, it means we can write an infinite loop like this:

while (( 1 ))
{
...
}

which “feels right” to a C programmer. But remember that the while statement is looking for a zero return—which it gets because (()) returns 0 for a true (i.e.,non-zero) result.

Before we leave the while loop, let’s take one more look at that while read example, which is reading from standard input (i.e., the keyboard), and see how it might get modified in order to read input from a file instead of the keyboard.

This is typically done in one of three ways. The first requires no real modifications to the statements at all. Rather, when the script is invoked, standard input is redirected from a file like this:

$ myscript <file.name

But suppose you don’t want to leave it up to the caller. If you know what file you want to process, or if it was supplied as a command-line argument to your script, then you can use this same while loop as is, but redirect the input from the file as follows:

while read lineoftext
do
    process that line
done < file.input

As a third way you might do this, you could begin by cat-ing the file to dump it to standard output, and then connect the standard output of that program to the standard input for the while statement:

cat file.input | 
while read lineoftext
doprocess that line
done

Warning

Because of the pipe, both the cat command and the while loop (including the process that line part), are each executing in their own separate subshells. This means that if you use this method, the script commands inside the while loop cannot affect the other parts of the script outside the loop. For example, any variables that you set within the while loop will no longer have those values after the loop ends. Such is not the case however if you use while read … done < file.input, because that isn’t a pipeline.

In the last example, the trailing backslash has no characters after it, just a newline. Therefore it escapes the newline, telling the shell to continue onto the next line without terminating the line. This is a more readable way to highlight the two different actions—the cat command and the while statement.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.102.182