'while head -n 1' 好奇心

'while head -n 1' curiosities

一些编码实验,(在尝试找到编码问题的较短答案时进行)导致了一些有趣的惊喜:

seq 2 | while head -n 1 ; do : ; done

输出(点击 Control-C 否则它将永远浪费 CPU 个循环):

1
^C

相同,但使用重定向输入文件而不是管道输入:

seq 2 > two
while head -n 1 ; do : ; done < two

输出(点击Control-C):

1
2
^C

问题:

  1. 为什么 while 循环不像 seq 2 | head -n 1 那样停止?

  2. 为什么 重定向输入 比 [=] 产生 更多 输出43=]管道输入?


以上代码在最近的 Lubuntu 上用 dashbash 进行了测试。 seqhead 都来自 coreutils(版本 8.25-2ubuntu2)包。

绕过必须按 (Ctrl-C):

的方法
timeout .1 sh -c "seq 2 > two ; while head -n 1 ; do : ; done < two"

1
2

timeout .1 sh -c "seq 2 | while head -n 1 ; do : ; done"

1

head -n 1,当在 stdin 上给定一个空流时,完全符合其权利和规范,可以立即以成功退出状态退出。

因此:

seq 2 | while head -n 1 ; do : ; done

...可以合法地永远循环,因为 head -n 1 不需要以非零状态退出并因此终止循环。 (只有 "an error occurred" 时标准才需要非零退出状态,并且文件行数少于输出请求的行数不被定义为错误)。

确实,这是明确的:

When a file contains less than number lines, it shall be copied to standard output in its entirety. This shall not be an error.


现在,if 您的 head 实现在第一次调用后(打印第一行的内容),使文件指针在当它退出时第二行的开头,(绝对不需要),然后第二个循环实例将读取第二行并发出它。然而,这又是一个实现细节,它取决于编写您的 head 实现的人是否选择 或者 :

  1. 读取一个非常大的块,但只发出它的一个子集。 (更有效的实施。)
  2. 逐字符循环以仅消耗一行。

实施者完全有权根据仅在运行时可用的标准来决定要遵循哪些实施。


现在,假设您的 head 总是 尝试一次读取 8kb 块。那么,它怎么能让指针在第二行排队呢? [* - 除了向后搜索,当给定文件时 some implementations do,但标准不需要;感谢 Rob Mayhoff 的指点]

这可能会发生如果 seq 的并发调用在第一个 read 发生时仅写入并刷新了一行

显然,这是一种对时间非常敏感的情况——一种竞争条件——并且还取决于未指定的实现细节,(seq 是否刷新其行间输出——如 seq 未指定为 POSIX 或任何其他标准的一部分,在平台之间完全不同。