“(head; tail)< file”如何工作?

时间:2012-12-05 07:23:39

标签: bash shell

(通过https://stackoverflow.com/a/8624829/23582

(head; tail) < file如何运作?请注意,cat file | (head;tail)没有。

另外,为什么(head; wc -l) < file0的输出提供了wc

注意:我理解头部和尾部是如何工作的。只是不是这些特殊调用涉及的微妙之处。

4 个答案:

答案 0 :(得分:17)

OS X

对于OS X,您可以查看source code for headsource code for tail以了解正在发生的一些事情。对于tail,您需要查看forward.c

因此,事实证明head没有做任何特别的事情。它只是使用stdio库读取其输入,因此它一次读取一个缓冲区并且可能读取太多。这意味着cat file | (head; tail)不适用于head缓冲使其读取最后10行中的部分(或全部)的小文件。

另一方面,tail检查其输入文件的类型。如果它是一个常规文件,tail寻找到最后并向后读,直到找到足够的行发出。这就是(head; tail) < file适用于任何常规文件的原因,无论大小如何。

的Linux

您也可以在Linux上查看headtail的来源,但更容易使用strace,如下所示:

(strace -o /tmp/head.trace head; strace -o /tmp/tail.trace tail) < file

看看/tmp/head.trace。您将看到head命令尝试通过读取标准输入(文件描述符0)来填充缓冲区(在我的测试中为8192个字节)。根据{{​​1}}的大小,它可能会也可能不会填充缓冲区。无论如何,我们假设它在第一次读取时读取了10行。然后,它使用file将文件描述符备份到第10行的末尾,基本上“读取”它读取的任何额外字节。这是有效的,因为文件描述符在普通的可搜索文件上打开。因此lseek适用于任何可搜索文件,但不会使(head; tail) < file正常工作。

另一方面,cat file | (head; tail) (在我的测试中)寻求结束并向后阅读,就像在OS X上一样。至少,它不会读取一直回到文件的开头。

这是我的测试。创建一个小的12行输入文件:

tail

然后,在Linux上尝试yes | head -12 | cat -n > /tmp/file 。我用GNU coreutils 5.97:

得到这个
(head; tail) < /tmp/file

但是在OS X上,我明白了:

     1  y
     2  y
     3  y
     4  y
     5  y
     6  y
     7  y
     8  y
     9  y
    10  y
    11  y
    12  y

答案 1 :(得分:7)

这里的括号创建一个subshell,它是运行内部命令的解释器的另一个实例,有趣的是子shell充当单个stdin / stdout组合;在这种情况下,它首先将stdin连接到head,它回显前10行并关闭管道,然后子shell将其stdin连接到tail,消耗其余的并将最后10行写回到stdout ,但子shell接受两个输出并将它们写为自己的标准输出,这就是它出现组合的原因。

值得一提的是,使用命令分组可以实现同样的效果,如{ head; tail; } < file更便宜,因为它不会创建另一个bash实例。

答案 2 :(得分:3)

如果文件足够大,所有这些都应该按预期工作。 head命令将消耗一定量的输入(不仅仅是它缓冲它的输入所需的内容),如果没有为tail命令留下足够的输入,它将无法工作。

另一个问题是管道导致双方并行执行,因此生产方可能会导致消费方的head命令在每次运行时读取不同的数量。

比较以下命令的多次运行:

for i in `seq 1 10`; do echo "foo"; done | (head -n1; wc -l)

wc命令每次都应该看到不同数量的文件。

当使用<提供输入时,似乎并不存在这种并行性(可能是bash读取整个输入然后将其传递给head命令)。

答案 3 :(得分:-2)

head命令显示前10行(默认)文件行。并且tail命令显示最后10行(默认)文件行。 假设文件只有3行也没问题,那些命令会显示这些行。 但是如果你有超过10行,那么这两个命令将只显示默认的10行。 将使用-n,n,+ n选项更改默认行数。 (参见手册页)