奇怪的问题:对于``while-read-loop循环和shell脚本中变量的可见性

时间:2011-05-25 09:46:52

标签: shell loops subprocess

我在从while循环内部更新shell脚本中的变量值时遇到问题。它可以使用以下代码进行模拟:

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

输出:

INSIDE: aaa
INSIDE: bbb
OUTSIDE: 

这里printf命令只显示两行,while-read循环逐行读取,更新某个变量,但是一旦控件退出循环,变量的值就会丢失。

我猜这个问题与'pipe-while-read'语句导致shell在子进程中执行循环体这一事实有关,该子进程无法更新主循环中的shell变量。

如果我将前两行代码重写为

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

输出:

INSIDE: aaa
INSIDE: bbb
OUTSIDE: bbb

这可能是一种解决方法,但不适用于我的情况,因为实际上我没有'aaa'和'bbb',而是更复杂的字符串,包括空格等。

知道如何解决这个问题,即:在循环中逐行读取命令输出并能够更新shell变量吗?

感谢。

2 个答案:

答案 0 :(得分:1)

您可以使用process substitution来取消管道输入:

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

或者,如果您的输入位于文件中,则可以将其重定向到while:

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

答案 1 :(得分:1)

摘自man bash

  

管道中的每个命令都作为一个单独的进程执行(即在子shell中)。

并且Subshel​​l无法更改Parent中的变量。

可能的解决方案之一是:

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

或者如果输入是文件:

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

一次读取一行,空格没有任何问题。