bash - 奇怪的管道缓冲
问题描述
我有一个充满文件编号的文件(从 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 精彩答案的补充,更多关于 stdio 缓冲
解决方案
发生的事情是 stdio 一次从管道读取整个缓冲区,而 Linux 上的缓冲区大小为 8K。
然后head
从缓冲区中读取前 10 行,打印它们,然后退出。
下一个head
开始从最后一个停止的管道读取 8K 字节到文件中。它读取该行和以下 9 行。60
你看到的是结束1860
。
在最后一种情况下它按预期工作的原因是因为head
它在退出之前寻找它打印的最后一行的末尾。寻找在管道中不起作用,所以这没有效果。但是当stdin
是普通文件时,seek 工作,下一个过程从seek 设置文件位置的地方开始。
我在 Mac 上看到的结果略有不同。它的缓冲区大小为 64K,因此第二个缓冲区head
在文件中的开始时间要晚得多。它也不会在退出之前回溯到最后打印行的末尾,因此具有文件重定向的版本与管道相同。
推荐阅读
- matlab - 创建一个 MATLAB 函数来确定客户在购买后应支付的零钱金额
- c - 如何监视 C 模块中的静态数据?
- c - 如何修复“错误:控制到达非无效函数的结尾”?
- gitlab - 在 gitlab 中将 cucumber.options 作为环境变量传递
- mysql - 计算 MySQL 中多个外部表的引用总数
- python - 获取当前时间之前的最近时间,该时间可被 5 整除
- apostrophe-cms - 从爬虫中排除特定区域
- swift - “RTCSdpTypeAnswer”和“RTCSdpTypePrAnswer”有什么区别?
- virtualbox - 如何解决 VirtualBox 桥接适配器问题?
- python - 如何使一定长度的单词翻转?