【问题标题】:Why doesn't `seq 100 | ( head -n1; tail -n1 )` work on Mac OSX?为什么没有`seq 100 | ( head -n1; tail -n1 )` 在 Mac OSX 上工作?
【发布时间】:2017-12-27 04:48:09
【问题描述】:

以下命令应该打印来自seq 100的第一行和最后一行,但它只打印第一行:

seq 100 | (head -n1 ; tail -n1)
1

它确实适用于更大的序列,例如 10,000:

seq 10000 | (head -n1 ; tail -n1)
1
10000

更新

我选择了@John1024 的答案,因为我的问题是为什么这不起作用,他提供了一个可以接受的答案。

另外,应该显然只是我的意见。现实是head 不能这样工作...它很可能消耗比我想要的更多的标准输入,并为tail留下任何东西。

当然,首先提示这个问题的问题是试图读取文件的第一行和最后 n 行。这是我基于 GNU sed 提出的解决方案:

sed -ne'1,9{p;b}' -e'10{x;s/$/--/;x;G;p;b}' -e':a;$p;N;21,$D;ba'

或更紧凑

sed -ne'1,9{p;b};10{x;s/$/--/;x;G;p;b};:a;$p;N;21,$D;ba'

示例输出:

*注意在我的 Mac 上,使用 MacPorts,GNU sed 被调用为 gsed。 Apple 的内置 sed 对分号分隔的表达式很挑剔,需要多个 -e 参数。这应该适用于 Apple 的 sed: sed -ne'1,9{' -e'p;b' -e'}' -e'10{' -e'x;s/$/--/;x;G;p;b' -e'}' -e':a' -e'$p;N;21,$D;ba' *

seq 100 | gsed -ne'1,9{p;b}' -e'10{x;s/$/--/;x;G;p;b}' -e':a;$p;N;21,$D;ba'

1
2
3
4
5
6
7
8
9
10
--
91
92
93
94
95
96
97
98
99
100

说明

gsed -ne' 调用 sed 没有自动打印模式空间

-e'1,9{p;b}' 打印前 9 行

-e'10{x;s/$/--/;x;G;p;b}' 打印第 10 行并附加“--”分隔符

-e':a;$p;N;21,$D;ba' 打印最后 10 行

【问题讨论】:

  • 如果你想只读一行并且消耗更多的输入,顺便说一句,你可以自己实现:seq 100 | { read first; echo "$first"; tail -n1; } 符合你的期望。
  • (顺便说一句,我对问题中的“应该”提出异议。如果你能找到一个书面保证,证明这里描述的行为会破坏,我很乐意看到它;head 符合的标准to 是here - 没有提供明显的保证:消耗了多少标准输入)。
  • 您可以通过使用单个程序打印第一行和最后一行来避免这个问题,例如seq 100 | sed -n '1p; $p'.
  • @CharlesDuffy 我理解你对“应该”的立场。我已经更新了我的答案,以反映“应该”只是我的意见。 head 不保证从标准输入“放回”未使用的字节这一事实是,IMO,不正确的行为,应该更新规范并修改 head
  • 没有机制可以批量“放回”内容。您要么一次只读取一个字节并且使事情变得非常慢(这就是read 的shell 实现所做的,以及为什么它的性能如此糟糕),或者您读取大块并消耗比您需要的更多。

标签: bash pipe seq


【解决方案1】:

我在 Linux 上看到 GNU headtail 的行为相同。

这取决于head -n1 在退出之前消耗了多少输入。如果head 在退出之前读取了所有的标准输入,那么tail 将没有任何内容可供读取,tail 不会产生任何输出。

观察:

$ seq 10000 | (head -n1 ; cat ) | head
1

1861
1862
1863
1864
1865
1866
1867
1868

在这里,我们可以看到head -n1 消耗了前 1860 行。 cat 命令查看所有剩余的输入。

这是为什么呢?观察前 1860 行中有多少字节:

$ seq 1860 | wc
   1860    1860    8193

合理的猜测是head -n1 首先从标准输入读取 8kB 的数据,然后打印第一行,并且看到它不需要更多数据,它就退出了。其余的标准输入可用于任何后续进程。

因此,seq 100 产生的总输出小于 8kB,head 读取所有标准输入,而没有任何内容可供tail 读取。对于产生超过 8kB 的seq 10000head 将不会读取管道中的所有数据。它留下的数据将提供给tail

正如Charles Duffy 指出的那样,此行为的细节完全取决于实现,并且在任何软件升级时,它都可能发生变化。

【讨论】:

  • 非常有趣的观察。
  • 我选择这个作为答案是因为我的问题是 为什么 它不起作用,而@John1024 提供了一个答案。我曾认为它一定是这种性质的东西,不幸的是,用管道实现头/尾的不可靠性使得使用它们是不可接受的。我已经用sed 脚本更新了我的答案,任何人都在寻找可行的解决方案来解决读取流的第一行和最后 n 行的(单独)问题。
猜你喜欢
  • 1970-01-01
  • 2018-02-20
  • 1970-01-01
  • 2014-05-07
  • 2021-04-20
  • 2014-10-31
  • 1970-01-01
  • 1970-01-01
  • 2011-04-25
相关资源
最近更新 更多