curious problem: for `` vs. while-read loops and v

2019-08-05 05:14发布

问题:

I have a problem updating a value of a variable in a shell script from inside of a while loop. It can be simulated with the following piece of code:

 printf "aaa\nbbb\n" | \
      while read x ; do
          y=$x
          echo "INSIDE: $y"
      done
 echo "OUTSIDE: $y"

Output:

INSIDE: aaa
INSIDE: bbb
OUTSIDE: 

Here printf command just display two lines, while-read loop read it line by line, updating certain variable, but as soon as control going out of the loop the value of the variable gets lost.

I guess the problem is related to the fact that 'pipe-while-read' statement causes shell to execute the body of the loop in a subprocess, which cannot update the shell variables in the main loop.

If I rewrite the first two lines of code as

 for x in `printf "aaa\nbbb\n" ` ; do

Output:

INSIDE: aaa
INSIDE: bbb
OUTSIDE: bbb

It could be a workaround, but not for my case because in reality I have not 'aaa' and 'bbb' but more complex strings including whitespaces etc.

Any idea how to tackle the problem, namely: read a command output line by line in a loop and be able to update shell variables?

Thanks.

回答1:

An excerpt from man bash:

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

And Subshell cannot change the variable in Parent.

One of the possible Solution is:

IFS='\n'
while read x ; do
   y=${x}
   echo "INSIDE: ${y}"
done <<EOT
aaa
bbb
EOT
echo "OUTSIDE: ${y}"

Or if the input is a file:

IFS='\n'
while read x ; do
   y=${x}
   echo "INSIDE: ${y}"
done < /path/to/file
echo "OUTSIDE: ${y}"

This reads one line at a time, and doesn't have any issue with spaces.



回答2:

You can get rid of the pipe-into-while by using process substitution instead:

while read x ; do
    y=$x
    echo "INSIDE: $y"
done < <(printf "aaa\nbbb\n")
echo "OUTSIDE: $y"

Alternatively, if your input is in a file, you can redirect it into while:

while read x ; do
    y=$x
    echo "INSIDE: $y"
done < file
echo "OUTSIDE: $y"