You want your shell script to perform some actions repeatedly as long as some condition is met.
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
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
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.
3.134.102.182