(通过https://stackoverflow.com/a/8624829/23582)
如何(head; tail) < file
工作?注意cat file | (head;tail)
不是。
另外,为什么要(head; wc -l) < file
给出0
的输出wc
?
注意:我了解头部和尾部的工作原理。只是不涉及这些特定调用的微妙之处。
(通过https://stackoverflow.com/a/8624829/23582)
如何(head; tail) < file
工作?注意cat file | (head;tail)
不是。
另外,为什么要(head; wc -l) < file
给出0
的输出wc
?
注意:我了解头部和尾部的工作原理。只是不涉及这些特定调用的微妙之处。
对于 OS X,您可以查看源代码head
和源代码tail
以了解发生了什么。在 的情况下tail
,您需要查看forward.c
。
所以,事实证明这head
并没有做任何特别的事情。它只是使用库读取其输入stdio
,因此它一次读取一个缓冲区并且可能读取太多。这意味着cat file | (head; tail)
不适用于head
's 缓冲使其读取最后 10 行中的部分(或全部)的小文件。
另一方面,tail
检查其输入文件的类型。如果它是一个常规文件,则tail
寻找到最后并向后读取,直到找到足够的行来发出。这就是为什么(head; tail) < file
适用于任何常规文件,无论大小。
你也可以查看 Linux 的源代码head
,tail
但使用起来更容易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
这里的括号创建一个subshell
解释器的另一个实例来运行内部命令,有趣的是子shell充当单个标准输入/标准输出组合;在这种情况下,它将首先将 stdin 连接到head
回显前 10 行并关闭管道,然后 subshell 将其 stdin 连接到tail
其消耗其余部分并将最后 10 行写回 stdout,但 subshell 获取两个输出并写入它们作为它自己的标准输出,这就是它看起来合并的原因。
值得一提的是,同样的效果可以通过命令分组{ head; tail; } < file
来实现,因为它不会创建另一个 bash 实例,因此更便宜。
如果文件足够大,所有这些都应该按预期工作。head 命令将消耗一定数量的输入(不仅仅是它在缓冲输入时所需要的),如果没有为 tail 命令留下足够的输入,它将无法工作。
另一个问题是管道导致双方并行执行,因此生产方可能会导致消费方的 head 命令在每次运行时读取不同的数量。
比较以下命令的多次运行:
for i in `seq 1 10`; do echo "foo"; done | (head -n1; wc -l)
wc 命令每次都应该看到不同数量的文件。
当使用 a<
提供输入时,似乎不存在这种并行性(可能 bash 读取整个输入,然后将其传递给 head 命令)。
head 命令显示文件的前 10 行(默认)。tail 命令显示文件的最后 10 行(默认)。假设如果文件只有 3 行也没有问题,这些命令将显示这些行。但是如果你有超过 10 行,那么这两个命令将只显示默认的 10 行。使用 -n、n、+n 选项将更改默认行数。(参考手册页)