使用 sed 或 awk 在两个正则表达式之间选择文件的块打印部分

Choose block print section of file between two regular expressions using sed or awk

sed -n '/pattern1/,/pattern2/p'

pattern1   
a   
b   
pattern2   
cd   
pattern1   
ef    
pattern2    
gh    
pattern1     
ef     
pattern2     

这将在所有匹配的 pattern1 和 pattern2 之间生成输出

但是我如何选择打印哪个块,比如只打印最后一个匹配的模式块或只打印第一个匹配的模式块?

打印第 N 次出现

这是您可以使用 awk 实现的一种方法:

$ awk '/pattern1/{++f;p=1}p&&f==2;/pattern2/{p=0}' file
pattern1
ef
pattern2

中间的数字 2 控制打印哪一个事件(在本例中为第二个)。

说明

当开始模式匹配时,f 递增并设置 p 标志。当结束模式匹配时,p 标志被取消设置。仅当设置 p 标志且 f 具有特定值时才会打印行。

如果您愿意,可以从 shell:

传递值
$ c=2
$ awk -v c="$c" '/pattern1/{++f;p=1}p&&f==c;/pattern2/{p=0}' file
pattern1
ef
pattern2

打印最后一次出现

要始终打印范围内的最后一次出现,您可以使用数组:

$ awk '{a[NR]=[=12=]}/pattern1/{s=NR}/pattern2/{e=NR}END{for(i=s;i<=e;++i)print a[i]}' file
pattern1
ef
pattern2

说明

文件中的每一行都按顺序存储在数组a中。每次匹配开始或结束模式时,se 都会被当前行号 NR 覆盖。最后,打印您感兴趣的元素。

这种方法的一个潜在缺点是整个文件的内容都存储在内存中,但除非您有非常大的文件,否则这可能不是问题。

Perl 来拯救!

正在打印最后一场比赛:

perl -ne 'push @keep, $_ if (/pattern1/ and @keep = ("")) .. /pattern2/;
          }{ print @keep'

说明:匹配保存在@keep中,匹配到pattern1时清空。因此,@keep 将在处理完整个输入后包含最后一个匹配项。

正在打印第 n 个匹配项:

perl -ne 'push @keep, $_
              if (/pattern1/ and ++$c and @keep = "")
              .. ($e = /pattern2/);
          print(@keep), last if $e and 2 == $c'
#                                      ^
#                                      |
#                                the second match

$c 计算匹配项。 $e 表示比赛结束。

另一种 awk 方式


找到第 n 次出现

awk -vM=2 '(x+=/pattern1/)==M&&x+=/pattern2/' file

输出

pattern1
ef
pattern2

说明

-vM=2

将 M 设置为您要查找的任何事件

(x+=/pattern1/)==M

每次出现 pattern1 时递增 x 并检查它是否等于 M.

&&x+=/pattern2/

如果确实如此,则每次出现 pattern2 时都会递增它,因此当它到达模式 2 时,它将打印该行,但不再打印,因为它现在将大于 M.

awk 的默认操作是打印。


打印最后一次出现

这只存储内存中看到的最后一个块。

awk 'x+=/pattern1|pattern2/{!y++&&B="";B=B?B"\n"[=15=]:[=15=];x==2&&y=x=0}END{print B}' file

输出

pattern1
ef
pattern2

说明

每次出现 pattern1 或 2 时增加 x
未设置 y 时刷新 B(当找到新集合时)然后设置 y
如果 x 存在,则将行添加到变量 B
如果计数为 2,则取消设置 x 和 y,这意味着两者都已被看到。