使用单个 sed 调用作为前 H 行的开头和最后 T 行的尾部

Using a single sed invocation to head the first H lines and tail the last T lines

前段时间我写了一个 C 程序来总结一个文本文件,同时执行 headtail,只有 一个 通读 管道 输入。示例:

$ headtail -h 3 -t 3 < /tmp/x10
line01
line02
line03
... 4 output lines omitted ...
line08
line09
line10

它可以工作,但我觉得没有漂亮的 sed 别名可以做到这一点,这让我觉得很脏。找到这个使用 sedprint the last N lines 的 SO 答案后,现在似乎可以实现,但我还不完全在那里。

例如,个人headtail工作:

$ sed -n -e '1,3p' < /tmp/x10
line01
line02
line03

$ sed -n -e ':a; $p; N; 4,$D; ba' < /tmp/x10
line08
line09
line10

但我将两者结合的尝试失败了:

$ sed -n -e '1,3p; :a; $p; N; 4,$D; ba' < /tmp/x10
line01
line08
line09
line10

如果 H+T > N[=36= 也能正常工作] 文件中的行(类似于 cat),并且它还打印一个分隔符,指示从中间省略了一些行(省略的数字会很好,但我可以没有它)。

尝试:

$ seq 10 | sed -n -e '1,3{p;b}; :a; $p; N; 7,$D; ba'
1
2
3
8
9
10

(73(头)加3(尾)加1的结果。)

如果我们将尾部从 3 增加到 7,我们将得到整个文件:

$ seq 10 | sed -n -e '1,3{p;b}; :a; $p; N; 12,$D; ba'
1
2
3
4
5
6
7
8
9
10

(123(头)加7(尾)加1。)

工作原理

  • 1,3{p;b}

    对于前三行中的任何一行,我们打印它们 (p),然后分支 (b) 通过代码中的其余命令。

  • :a; $p; N; 7,$D; ba

    这与以前的工作方式相同 除了这些行永远不会看到前三行。因此,我们必须将 D 命令的起点更改为 7.

不需要 C 程序或复杂的 sed 脚本,您只需要一个清晰、简单、可移植、高效的 awk 脚本:

$ seq 10 | awk -v h=3 -v t=3 'NR<=h; {a[NR%t]=[=10=]} END{for (i=1; i<=t; i++) print a[(NR+i)%t]}'
1
2
3
8
9
10

$ seq 10 | awk -v h=3 -v t=3 'NR<=h; {a[NR%t]=[=10=]} END{print "skipped", NR-(t+h); for (i=1; i<=t; i++) print a[(NR+i)%t]}'
1
2
3
skipped 4
8
9
10

如果范围重叠,你没有说明你的要求是什么,所以我只是在两个输出部分中包括重叠线,并为跳过打印一个负值,例如:

$ seq 10 | awk -v h=7 -v t=5 'NR<=h; {a[NR%t]=[=11=]} END{print "skipped", NR-(t+h); for (i=1; i<=t; i++) print a[(NR+i)%t]}'
1
2
3
4
5
6
7
skipped -2
6
7
8
9
10

但是无论您对边缘情况的要求是什么,它们的实现都是微不足道的。

这可能适合您 (GNU sed):

sed -E '1,5p;H;$!d;x;s/.*((\n[^\n]*){3})$//;s/./==========&/' file

这将打印由 ========== 分隔的前五行和最后三行。

命令使用前 n 行的范围,所有行都存储在保留 space 中。在文件末尾,保留 space 减少到所需的行数,并且前导换行符被分隔符替换。

另一种解决方案,内存占用较少但仅限于标题线等于或小于尾线是:

sed ':a;$!{N;;s/[^\n]\+/&/5;3{p;x;s/^/==========/p;x};Ta};$P;D' file

此处前三行和后五行用分隔符打印。