[comp.unix.shell] problems with 'while read'

wib@informatik.uni-kiel.dbp.de (Willi Burmeister) (05/06/91)

Just a simple (??) question: Why does this small program not work?

------ cut here ------ cut here ------ cut here ------ cut here ------
#!/bin/sh
#

cat << EOF > hugo
one
two
three
four
EOF

while read num
do
  echo "do something with <$num>"
  last=$num
done < hugo

echo "last num = <$last>"
------ cut here ------ cut here ------ cut here ------ cut here ------ 

the output will always be:

do something with <one>
do something with <two>
do something with <three>
do something with <four>
last num = <>


What I want is:

do something with <one> 
do something with <two> 
do something with <three> 
do something with <four> 
last num = <four> 
            ^^^^

Any help would be much appreciated.
thanks in advance


Willi


+------------------+----------------------------------------+
| Willi Burmeister | e-mail: wib@informatik.uni-kiel.dbp.de |
|  Holzkrugweg 30  | phone office: +49 0431 560498          |
| D-2390 Flensburg | phone home:   +49 0461 94166           |
+------------------+----------------------------------------+

FFAAC09@cc1.kuleuven.ac.be (Nicole Delbecque & Paul Bijnens) (05/07/91)

In article <3635@dagobert.informatik.uni-kiel.dbp.de>,
wib@informatik.uni-kiel.dbp.de (Willi Burmeister) says:
>
>Just a simple (??) question: Why does this small program not work?
>
>------ cut here ------ cut here ------ cut here ------ cut here ------
>#!/bin/sh
>
>cat << EOF > hugo
>one
>two
>three
>four
>EOF
>
>while read num
>do
>  echo "do something with <$num>"
>  last=$num
>done < hugo
>
>echo "last num = <$last>"
>------ cut here ------ cut here ------ cut here ------ cut here ------
>
>the output will always be:
>
>do something with <one>
>do something with <two>
>do something with <three>
>do something with <four>
>last num = <>
>
>
>What I want is:
>
>do something with <one>
>do something with <two>
>do something with <three>
>do something with <four>
>last num = <four>
>            ^^^^

You are using the Bourne shell.  This shell implements the redirection
of the while-loop with a subshell.  And as you probably already
know: you cannot get the variable from the subshell to the parent shell.
(Read also the FAQ about this problem.)
I have been told the Korn shell does this right...

One way round this, using an ugly temporary file:

    trap "rm -f /tmp/sh$$; exit" 0 1 2 3 15
    while read num
    do
        echo "Do something with $num"
        echo $num > /tmp/sh$$
    done << EOF
    one
    two
    three
    EOF
    read last < /tmp/sh$$
    echo last is $last

A better way is to do some manipulation with the filedescriptors.
We can avoid the redirection of the loop like this:

    cat > hugo  << EOF
    one
    two
    three
    EOF

    exec 3<&0 0<hugo   # duplicate the standard input filedescriptor
                       # and redirect it then to read from the file

    while read num     # standard input now reads from file "hugo"
    do
        echo "Doing $num"
        last=$num
    done

    exec 0<&3-         # restore standard input from 3, and close fd 3

    echo last is $last

et voila, it works.  (At least, it works with me.)
--
Polleke   (Paul Bijnens)
Linguistics dept., K. University Leuven, Belgium
FFAAC09@cc1.kuleuven.ac.be

les@chinet.chi.il.us (Leslie Mikesell) (05/07/91)

In article <3635@dagobert.informatik.uni-kiel.dbp.de> wib@informatik.uni-kiel.dbp.de (Willi Burmeister) writes:

>while read num
>do
>  echo "do something with <$num>"
>  last=$num
>done < hugo
>
>echo "last num = <$last>"

This creates a subshell to run the redirected loop. Thus, outside of
the loop, variables are unaffected.  If no other redirections
are needed, just redirect the whole script.  If they are, you may
be able to use parens to force the subshell to contain the part that
needs the affected variable:

(
while read num
do
  echo "do something with <$num>"
  last=$num
done
echo "last num = <$last>"
) < hugo

Les Mikesell
  les@chinet.chi.il.us