3

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

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. 为什么重定向输入会比管道输入产生更多的输出?


上面的代码在最近的Lubuntudash上进行了测试。两者都来自coreutils(版本8.25-2ubuntu2)包。bashseqhead

绕过必须按(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

4

1 回答 1

3

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

因此:

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

...可以合法地永远循环,因为head -n 1不需要以非零状态退出并因此终止循环。(仅当“发生错误”时,标准才需要非零退出状态,并且文件的行数少于输出请求的行数未定义为错误)。

确实,这是明确的:

当文件包含的行数少于行数时,应将其全部复制到标准输出。这不应是错误。


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

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

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


现在,假设您head 总是尝试一次读取 8kb 块。那么,它怎么可能让指针排队等待第二行呢?[ * - 除了向后搜索,某些实现在给定文件时会这样做,但标准没有要求;感谢Rob Mayhoff提供的指针]

如果在第一次发生时的并发调用seq仅写入和刷新了一行,则可能read会发生这种情况

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

于 2016-07-06T02:33:46.783 回答