使用单个sed调用来开头的前H行和结尾的最后T行

时间:2019-02-22 22:26:53

标签: awk sed text-processing

我不久前写了一个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 行(如{{1 }},并为其打印一个分隔符,指示中间省略了一些行(省略的数字会很好,但是我可以不用它)。

3 个答案:

答案 0 :(得分:1)

尝试:

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

({7来自于3(头)加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

答案 1 :(得分:1)

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

$ seq 10 | awk -v h=3 -v t=3 'NR<=h; {a[NR%t]=$0} 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]=$0} 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]=$0} 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

但是无论您对边缘案例有什么要求,实现起来都是微不足道的。

答案 2 :(得分:1)

这可能对您有用(GNU sed):

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

这将打印出前五行和后三行,用==========分隔。

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

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

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

这里的前三行和最后五行用分隔符打印。