为什么在“while”循环中使用“read变量”设置变量时是全局变量,而使用“while read变量”设置变量时是局部变量?

为什么在“while”循环中使用“read变量”设置变量时是全局变量,而使用“while read变量”设置变量时是局部变量?
unset myVariable i;
while [ -z "$i" ]; do
    read myVariable;
    echo "myVariable : '$myVariable'";
    i=foo;
done;
echo "myVariable : '$myVariable'"

unset允许重播命令)

按任意键 + ENTER,您将得到:

myVariable : '[what you typed]'
myVariable : '[what you typed]'

的值myVariable存在于while循环之外。现在尝试:

tmpFile=$(mktemp);
echo -e 'foo\nbar\nbaz' >> "$tmpFile"
while read myVariable; do
    otherVariable=whatever; 
    echo "myVariable : '$myVariable', otherVariable : '$otherVariable'";
done < "$tmpFile";
echo "myVariable : '$myVariable', otherVariable : '$otherVariable'";
rm "$tmpFile"

你会得到 :

myVariable : 'foo', otherVariable : 'whatever'
myVariable : 'bar', otherVariable : 'whatever'
myVariable : 'baz', otherVariable : 'whatever'
myVariable : '', otherVariable : 'whatever'

myVariable当离开循环时,的值就会丢失。

为什么会有不同的行为?有我不知道的范围技巧吗?

注意:运行 GNU bash,版本 4.4.12(1)-release (x86_64-pc-linux-gnu)

答案1

while read myVariable; do

myVariable当离开循环时,的值就会丢失。

不,myVariable具有从上次获得的值read。该脚本从文件中读取,直到到达最后一个换行符之后的位置。之后,最终read调用不会从文件中获取任何内容,myVariable相应地设置为空字符串,并以错误值退出,因为它没有看到分隔符(换行符)。然后循环结束。

read如果最后一个换行符后面有不完整的行,您可以从最终得到一个非空值:

$ printf 'abc\ndef\nxxx' | 
    { while read a; do echo "got: $a"; done; echo "end: $a"; } 
got: abc
got: def
end: xxx

或者用于while read a || [ "$a" ]; do ...处理循环体内的最后一行片段。

答案2

因为您正在通过管道连接到 while 循环,所以会创建一个子 shell 来运行 while 循环。现在,这个子进程拥有自己的环境副本,并且无法将任何变量传递回其父进程(就像在任何 UNIX 进程中一样)。

在这种情况下,done < "$tmpFile"重定向会强制 bash 生成一个子 shell(对于管道)。

引自bash 变量作用域在堆栈溢出上。

答案3

您的第二个示例使用while具有重定向输入的循环。

它读取 using< "$tmpFile"并且许多 shell 为这种情况创建了一个子 shell。您可以尝试使用 运行此脚本ksh93ksh93在这种情况下不会创建子 shell。

在您的具体情况下,原因完全不同:

  • 在第一个示例中,您从输入中读取一行

  • 在第二个示例中,您读取直到EOF达到

read命令读取输入,然后按字符分割输入IFS,然后将单词分配给 的变量参数read

如果作为命令参数的单词数量多于变量read,则最后一个变量将获得其余单词的串联。

如果单词数少于变量,则将其他变量分配为空值。

自从你点击 后EOF,你就没有读取到任何单词,而是将一个或多个变量作为 的参数read。这会导致所有变量分配空值。

因此发生了您意想不到的事情:EOF导致while循环终止,并且您echo在循环内看不到任何命令,而在这种情况下只看到循环后的while最终命令。echowhileEOF

这个决赛echo现在打印从击中清除的变量内容EOF

相关内容