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

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

我的一个 shell 脚本出了问题。我问过几个同事,但他们都摇头(挠头之后),所以我来这里寻求答案。

根据我的理解,以下 shell 脚本应该打印“Count is 5”作为最后一行。但事实并非如此。它打印“Count is 0”。如果将“while read”替换为任何其他类型的循环,它就可以正常工作。以下是脚本:

echo "1">输入数据
echo "2">>输入数据
echo "3">>输入数据
echo "4">>输入数据
echo "5">>输入数据

碳氮=0

cat 输入.数据 | 读取时;
  让 CNT++;
  echo "计数到 $CNT"
完毕
echo "计数为 $CNT"

为什么会发生这种情况?我该如何防止?我在 Debian Lenny 和 Squeeze 中尝试过,结果相同(即 bash 3.2.39 和 bash 4.1.5。我完全承认自己不是 shell 脚本专家,因此任何指点都将不胜感激。

答案1

参见论点@Bash FAQ 条目 #24:“我在循环中设置了变量。为什么它们在循环终止后突然消失?或者,为什么我无法通过管道将数据传输到 read?”(最近存档这里)。

摘要:此功能仅支持 bash 4.2 及更高版本。如果您使用 bash,则需要使用其他方式,例如命令替换而不是管道。

答案2

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

将最后一个echo与子壳组合在一起while以修复它(还有许多其他方法可以修复它,这是其中之一。Iain 和 Ignacio 的答案还有其他。)

CNT=0

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

详细解释:

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

答案3

这有效

CNT=0 

while read ;
do
  let CNT++;
  echo "Counting to $CNT"
done <input.data
echo "Count is $CNT"

答案4

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

我说,如果问题出现在管道的最后一段,而在 Bash 中,管道中的所有命令都在一个相互隔离的独立进程中的子 shell 中执行,那么使用将lastpipe在前台执行管道中的最后一条命令。

例如:

CNT=0
shopt -s lastpipe
cat input.data | while read ;
...

几乎所有事物都保持不变。

相关内容