为什么此SH脚本生成以下输出

时间:2009-08-10 18:59:56

标签: shell

代码:

while read line;
do
  tr 3 4
done<<EOF
1
2
3
4
EOF

 生产:

2
4
4

1去哪儿了?

3 个答案:

答案 0 :(得分:7)

$ while read line
> do
>     tr 3 4
> done<<EOF
> 1
> 2
> 3
> 4
> EOF
2
4
4

这里发生的是read line获得1的第一行。 输入的其余部分传递给tr,将4替换为3,因此2 3 4变为2 4 4

1进入变量线。实施例

$ while read foo; do echo "This is $foo"; done <<EOF
> madness
> SPARTAAAAAAAAAAAA
> EOF
This is madness
This is SPARTAAAAAAAAAAAA

您看到的奇怪行为是因为阅读是内置的,并且有些特殊。例如,当你执行管道时,假设你写了

echo "foo bar" | read i

你会期待$ i中的某些东西,对吗?试试吧,它会是空的。为什么? 当您进行管道连接时,管道的每个命令都在子shell中运行。这对所有事情都是如此,而且永远如此。

读取有点奇怪,因为它不是命令,它是shell的内置,它必须导出变量才能看到它。如果您使用管道,如上例所示,它将在子进程(子shell)中导出i,您将永远不会看到它,因为您正在使用的shell环境是父级,而子shell不能设置其父级的环境。

在您的情况下,会发生的事情是readwhile一起构建在您的shell中。 read尽职尽责地从stdin获取第一个东西并将其设置为同一个shell的变量。这将从stdin中删除1,因为这是read builtin所做的。 stdin现在留下剩余的东西,它被传递给tr,因为在那时,stdin被第一个命令拦截。试试这个

$ while true; do rev; echo "x"; done <<EOF
> hello
> EOF
olleh

你会留下一堆x。 rev已经完成(检查其在第一行中的输出),并且stdin已关闭(您使用EOF执行此操作)。 while循环将永远继续(你会得到一个x级联)但你不能再提供任何东西了,因为你不再输入stdin了。

答案 1 :(得分:3)

这是Stephano回答的后续内容 - 所有的功劳都应归功于他。这是你如何理解这一点:

首先你要求sh'读'一条线,所以它确实如此。然后你问它'tr 3 4'。现在,'read'只读取一行,但'tr'将读取所有输入,直到它完成,所以当你回到'read'时,stdin中没有更多内容,程序就完成了。因为你从来没有告诉sh回显$ line,你输出的结果不会是1。

以下内容应打印1 2 4 4

while read line
do
   echo $line | tr 3 4
done

答案 2 :(得分:2)

斯特凡诺的回答非常好。 也许你想这样做?

tr 3 4 <<EOF
1
2
3
4
EOF