我的一个 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" )
详细解释:
CNT
您在脚本上声明其值为0;|
在上启动了一个 SubShellwhile read
;- 您的
$CNT
变量以值0导出到SubShell; - SubShell 计数并将
CNT
值增加到 5; - SubShell 结束,变量和值被销毁(它们不会返回到调用进程/脚本)。
- 你
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 ;
...
几乎所有事物都保持不变。