【问题标题】:Strange pipe buffering奇怪的管道缓冲
【发布时间】:2019-08-12 02:39:42
【问题描述】:

我有一个满是文件编号的文件(从 0 开始)

$ cat in.del
0
1
2
....

谁能解释这里发生了什么以及缓冲发生在管道以外的地方吗?据我了解,head's fileno(stdin) 都必须直接查看管道的读取端

$ cat in.del | ( head -n1 ; head -n1 )
0
60

下面的代码与上面的代码有何不同?

$ cat in.del | ( head -n10 ; head -n10 )
0
1
...
8
9
60
1861 # O_o
1862
1863
...
1868
1869

这按预期工作,并表明 head 本身读取的字节数并不多于它实际写入其 stdout 的字节数:

$ ( head -n10 ; head -n10 ) < ./in.del
0
1
...
9
10
11
...
18
19

显然有一些与管道有关的事情发生

更新

操作系统:Ubuntu 18.04.1 LTS

Bash:版本 4.4.19(1)-release (x86_64-pc-linux-gnu)

更新 2 作为@Barmar 精彩回答的补充,more on stdio buffering

【问题讨论】:

  • 在我的 Mac 上,最后一个示例与第二个示例相同。但是在 Debian Linux 上我得到的结果和你一样。
  • @Barmar,用系统信息更新了问题。另外,我确认您在 Mac 上描述的相同行为(最后一个示例给出与第二个相同的输出),但是输出了不同的行号
  • 我偶然发现了这个问题,因为我遇到了类似的问题。我需要使用variable=$(head -n 1) 为我的脚本提供管道支持。我发现的解决方法是改用read -r variable。这似乎也适用于上述示例cat in.del | ( read -r line; echo $line ; read -r line ; echo $line ),以防其他人(或将来的我)仍然需要他们脚本中的管道支持。

标签: bash unix pipe file-descriptor


【解决方案1】:

实际情况是 stdio 一次从管道中读取整个缓冲区,而 Linux 上的缓冲区大小为 8K。

然后head 从缓冲区中读取前 10 行,打印出来,然后退出。

下一个head 开始从最后一个停止的管道读取 8K 字节到文件中。它读取该行和以下 9 行。你看到的601860的结尾。

它在最后一种情况下按预期工作的原因是因为head 在它退出之前寻找它打印的最后一行的末尾。寻找在管道中不起作用,所以这没有效果。但是当stdin是一个普通文件时,seek工作,下一个过程从seek设置文件位置的地方开始。

我在 Mac 上看到的结果略有不同。它的缓冲区大小为 64K,因此第二个head 在文件中的开始时间要晚得多。它也不会在退出前回溯到最后打印行的末尾,因此带有文件重定向的版本与管道相同。

【讨论】:

  • 有道理,谢谢!我的实验设置很好:一旦我发现这是由于缓冲,我应该将行号更改为 ^&lt;line_number&gt;$ 并且会看到 60 不适用于行号 60
  • 是的,第一次测试真的很混乱。在第二个测试中,可以看到下一行是1861,前一行应该是1860
  • 顺便说一句,这个词是“缓冲”,而不是“缓冲”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多