30

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

如何(head; tail) < file工作?注意cat file | (head;tail)不是。

另外,为什么要(head; wc -l) < file给出0的输出wc

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

4

4 回答 4

19

操作系统

对于 OS X,您可以查看源代码head源代码tail以了解发生了什么。在 的情况下tail,您需要查看forward.c

所以,事实证明这head并没有做任何特别的事情。它只是使用库读取其输入stdio,因此它一次读取一个缓冲区并且可能读取太多。这意味着cat file | (head; tail)不适用于head's 缓冲使其读取最后 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 字节)。根据 的大小file,它可能会也可能不会填充缓冲区。无论如何,让我们假设它在第一次读取时读取了 10 行。然后,它使用lseek将文件描述符备份到第 10 行的末尾,基本上“未读取”它读取的任何额外字节。这是有效的,因为文件描述符是在一个普通的、可搜索的文件上打开的。所以(head; tail) < file适用于任何可搜索的文件,但它不会cat file | (head; tail)起作用。

另一方面, (在我的测试中)tail不会像在 OS X 上那样寻找到最后并向后读取。至少,它不会一直读回文件的开头。

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

yes | head -12 | cat -n > /tmp/file

(head; tail) < /tmp/file然后,在 Linux 上尝试。我用 GNU coreutils 5.97 得到了这个:

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

但在 OS X 上,我得到了这个:

     1  y
     2  y
     3  y
     4  y
     5  y
     6  y
     7  y
     8  y
     9  y
    10  y
     3  y
     4  y
     5  y
     6  y
     7  y
     8  y
     9  y
    10  y
    11  y
    12  y
于 2012-12-06T04:27:54.857 回答
9

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

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

于 2013-04-25T01:51:35.747 回答
4

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

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

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

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

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

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

于 2012-12-05T14:51:25.433 回答
-2

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

于 2012-12-05T15:04:52.070 回答