|
Chapter 45 Shell Programming for the Initiated
|
|
The Bourne shell usually runs
a loop with redirected input or output (45.22
)
in a
subshell (38.4
)
.
For the formprog
script in article
45.22
,
this means, among other things, that:
Any command inside the loop that reads its standard input will
read from the pipe or file redirected to the loop's standard input.
That's something you have to pay attention to, because the only command that
should read from the file is usually the read
command at the top
of the loop.
The inputs of other commands inside the loop - like commands that read
from the terminal - have to be redirected to read from somewhere other
than the loop's standard input.
In many Bourne shells, if you use the
exit
(38.4
)
command inside a
redirected loop, that will only terminate the subshell that's running
the loop; it will not
terminate the
script.
It's hard to call this a "feature"; I'd call it a bug.
The script in article
45.22
has a workaround for this; see the next paragraph.
Later versions of Bourne-like shells have fixed this problem, more or less,
but the fix below should work in all Bourne shells.
If there's any error inside the loop that should terminate the script,
an error message is written to file descriptor 2.
File descriptor 2 is redirected to an error-holding file at the subshell
(loop) output.
A break
command can end the loop right away.
After the loop ends, if the error file has anything in it, that means
there was an error - if there are more commands to run, the script can
terminate before running them.
You can test the
exit status (44.7
)
of the redirected-I/O loop.
To end the loop, use a command like exit 0
, exit 2
,
and so on.
Just after the done
command outside the loop, use
case $?
(44.5
)
to test the loop's status.
For instance, a 0 status might mean the loop worked fine, a 1 could
signal one kind of error, a 2 status a different error, and so on.
If you change the value of any shell or environment variables inside the
loop, their values outside the loop (after the done
command at
the end of the loop) will not be changed.
Here's the usual fix for that problem.
You use another file descriptor, like file descriptor 6,
and write variable-setting
commands to it.
You redirect that file descriptor to a temporary file.
Then, use the shell's
dot command (.
) (44.23
)
to read the temporary file into the shell outside the loop.
For example, to get the value of a variable named varname
outside the
loop:
while whatever
do ...
echo "varname='value'" 1>&6
...
done 6> var_set_file
. var_set_file
Greg Ubben sent me two other ways that he prefers.
The first one depends on having a read
that accepts redirection
on its command line, which most do these days.
The second works when you can put the usage in the same scope (within
the
curly braces (13.8
)
)
as the redirection:
exec 3< file {
while read line <&3 while read line
do do
var=value var=value
done done
exec 3<&- echo "var = $var"
echo "var = $var" } < file
Putting the loop inside a function and redirecting into the function
also seems to avoid the subshell problem.
But don't take my (our) word for it:
test it on the shell you'll be using.
|
|