bash:变量在读取循环结束时丢失值

wol*_*gsz 51 bash scope

我的一个 shell 脚本有问题。问了几个同事,他们都只是摇头(挠了挠头),所以我来这里是为了回答。

根据我的理解,下面的 shell 脚本应该打印“Count is 5”作为最后一行。除了它没有。它打印“计数为 0”。如果将“while read”替换为任何其他类型的循环,它就可以正常工作。这是脚本:

echo "1">input.data
回声“2”>>输入.数据
回声“3”>>输入.数据
echo "4">>input.data
回声“5”>>input.data

碳纳米管=0 

cat input.data | 阅读时;
做
  让 CNT++;
  echo "计数到 $CNT"
完毕 
echo "计数是 $CNT"

为什么会发生这种情况,我该如何预防?我已经在 Debian Lenny 和 Squeeze 中尝试过这个,结果相同(即 bash 3.2.39 和 bash 4.1.5。我完全承认不是一个 shell 脚本向导,所以任何指针都将不胜感激。

cor*_*ump 40

这是一种“常见”错误。管道创建子外壳,因此while read它在与脚本不同的外壳上运行,这使您的CNT变量永远不会改变(只有管道子外壳内的变量)。

将最后一个echo与子shell分组while以修复它(还有许多其他方法可以修复它,这是一种。Iain 和 Ignacio 的答案还有其他方法。)

CNT=0

 cat input.data | ( while read 
do
  let CNT++;
  echo "Counting to $CNT"
done 
echo "Count is $CNT" )
Run Code Online (Sandbox Code Playgroud)

长解释:

  1. CNT在脚本中声明值为 0;
  2. 子shell启动的|while read;
  3. 您的$CNT变量以值 0 导出到 SubShell;
  4. SubShell 计数并将CNT值增加到 5;
  5. SubShell 结束,变量和值被销毁(它们不会返回到调用进程/脚本)。
  6. echo的原始CNT值是 0。

  • 我写的第一个 shell 脚本给了我同样的问题,在发现管道产生额外的 shell 之前,我的头撞在墙上了一段时间。一旦管道结束,您在管道中弄乱的任何变量都会超出范围 - 这意味着如果您真的,真的想对使用它的管道之外的变量做一些事情,您将不得不通过像临时文件这样时髦的东西来保持状态。 (2认同)

Ign*_*ams 31

请参阅参数@ Bash FAQ 条目 #24:“我在循环中设置变量。为什么在循环终止后它们突然消失?或者,为什么我不能通过管道读取数据?” (最近存档在这里)。

总结:这仅从 bash 4.2 及更高版本支持。如果您使用 bash,则需要使用不同的方式,例如命令替换而不是管道。

  • 链接已死。这就是仅链接答案不好的原因。至少在这里总结一下答案。 (7认同)

use*_*517 11

这有效

CNT=0 

while read ;
do
  let CNT++;
  echo "Counting to $CNT"
done <input.data
echo "Count is $CNT"
Run Code Online (Sandbox Code Playgroud)


Ste*_*eve 7

尝试在子 shell 中传递数据,就像它是 while 循环之前的文件一样。这类似于 lain 的解决方案,但假设您不想要一些间歇性文件:

total=0
while read var
do
  echo "variable: $var"
  ((total+=var))
done < <(echo 45) #output from a command, script, or function
echo "total: $total"
Run Code Online (Sandbox Code Playgroud)


Cua*_*tli 5

另一个解决方案是简单地shopt -s lastpipe在 while 循环之前添加。

我说,如果出现麻烦,因为 while 位于管道的最后一段,并且在 Bash 中,管道中的所有命令都在彼此隔离的单独进程的子 shell 中执行,然后使用将执行最后lastpipe一个前台管道中的命令。

例如:

CNT=0
shopt -s lastpipe
cat input.data | while read ;
...
Run Code Online (Sandbox Code Playgroud)

几乎一切都保持不变。