打印两个模式之间的所有行,仅排他,仅第一实例(在sed,AWK或Perl中)

时间:2019-03-18 11:33:53

标签: bash perl awk sed

使用sed,AWK(或Perl),如何打印两个模式(第一个实例)之间的所有行(不包括模式)? 1

也就是说,作为输入给出:

aaa
PATTERN1
bbb
ccc
ddd
PATTERN2
eee

甚至可能:

aaa
PATTERN1
bbb
ccc
ddd
PATTERN2
eee
fff
PATTERN1
ggg
hhh
iii
PATTERN2
jjj

在两种情况下,我都会期望:

bbb
ccc
ddd

1 许多用户投票结束了该问题,并重复了this个问题。最后,我提供了gist,以证明它们是不同的。这个问题从表面上也类似于a number of others,但是没有精确匹配,而且都不是高质量的,而且我相信这个特定的问题是最常见的问题,它应该有一个清晰的表述,以及一系列正确,清晰的答案。

6 个答案:

答案 0 :(得分:6)

如果您具有GNU sed(在Mac OS X上使用4.7版进行了测试),最简单的解决方案可能是:

sed '0,/PATTERN1/d;/PATTERN2/Q'

说明:

  • d命令从第1行删除到与/PATTERN1/相匹配的行。
  • 然后Q命令退出,而不在与/PATTERN2/匹配的第一行上打印。

如果文件只有一个模式实例,或者如果您不介意提取所有模式实例,并且想要一种不依赖于GNU扩展的解决方案,则可以这样做:

sed -n '/PATTERN1/,/PATTERN2/{//!p}'

说明:

  • 请注意,空的正则表达式//会重复最后一次匹配的正则表达式。

答案 1 :(得分:3)

使用awk(假设PATTERN1PATTERN2始终成对出现,并且其中一个都不成对出现)

$ cat ip.txt
aaa
PATTERN1
bbb
ccc
ddd
PATTERN2
eee
fff
PATTERN1
ggg
hhh
iii
PATTERN2
jjj

$ awk '/PATTERN2/{exit} f; /PATTERN1/{f=1}' ip.txt
bbb
ccc
ddd
    如果/PATTERN1/{f=1}
  • /PATTERN1/设置标志
  • 如果/PATTERN2/{exit}
  • /PATTERN2/退出
  • f;打印输入行(如果设置了标志)


通用解决方案,可以在其中指定所需的块

$ awk -v b=1 '/PATTERN2/ && c==b{exit} c==b; /PATTERN1/{c++}' ip.txt
bbb
ccc
ddd
$ awk -v b=2 '/PATTERN2/ && c==b{exit} c==b; /PATTERN1/{c++}' ip.txt
2
46

答案 2 :(得分:2)

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

sed -n '/PATTERN1/{:a;n;/PATTERN2/q;p;$!ba}' file

这仅打印第一组定界符之间的行,或者如果第二个定界符不存在,则显示到文件末尾的行。

答案 3 :(得分:2)

我试图回答两次,但是问题切换为保持/重复状态。

借用@Sundeep的输入并添加我在问题注释中共享的答案。

使用awk

awk -v x=0 -v y=1 ' /PATTERN1/&&y { x=1;next } /PATTERN2/&&y { x=0;y=0; next } x ' file

使用Perl

perl -0777 -ne ' while( /PATTERN1.*?\n(.+?)^[^\n]*?PATTERN2/msg ) { print $1 if $x++ <1 } '

结果:

$ cat ip.txt
aaa
PATTERN1
bbb
ccc
ddd
PATTERN2
eee
PATTERN1
2
46
PATTERN2
xyz

$

$ awk -v x=0 -v y=1 ' /PATTERN1/&&y { x=1;next } /PATTERN2/&&y { x=0;y=0; next } x ' ip.txt
bbb
ccc
ddd

$ perl -0777 -ne ' while( /PATTERN1.*?\n(.+?)^[^\n]*?PATTERN2/msg ) { print $1 if $x++ <1 } ' ip.txt
bbb
ccc
ddd

$

使其具有通用性

awk在这里y是输入

awk -v x=0 -v y=2 ' /PATTERN1/ { x++;next } /PATTERN2/ { if(x==y) exit } x==y ' ip.txt
2
46

perl检查++ $ x的出现。在这里是2

perl -0777 -ne ' while( /PATTERN1.*?\n(.+?)^[^\n]*?PATTERN2/msg ) { print $1 if ++$x==2 } ' ip.txt
2
46

答案 4 :(得分:1)

添加更多解决方案(这里可能是有趣的方式:),并且一点也不声称这些解决方案比通常的解决方案更好)。所有测试和编写均使用GNU awk。还仅通过给定的示例进行了测试。

第一个解决方案:

awk -v RS="" -v FS="PATTERN2" -v ORS="" '$1 ~ /\nPATTERN1\n/{sub(/.*PATTERN1\n/,"",$1);print $1}' Input_file

第二个解决方案:

awk -v RS="" -v ORS="" 'match($0,/PATTERN1[^(PATTERN2)]*/){val=substr($0,RSTART,RLENGTH);gsub(/^PATTERN1\n|^$\n/,"",val);print val}' Input_file

第三种解决方案:

awk -v RS="" -v OFS="\n" -v ORS="" 'sub(/PATTERN2.*/,"") && sub(/.*PATTERN1/,"PATTERN1"){$1=$1;sub(/^PATTERN1\n/,"")} 1' Input_file

在以上所有代码中,输出如下。

bbb
ccc
ddd

答案 5 :(得分:1)

使用GNU sed:

sed -nE '/PATTERN1/{:s n;/PATTERN2/q;p;bs}'

-n将修剪除PATTERN1和PATTERN2之间的所有行(包括这两者),因为会有p打印输出命令。 每次sed范围检查是否属实,下一次只会执行一次,因此必须进行{}分组。 通过n命令放下PATTERN1(意味着下一个),如果完全到达第一个PATTERN2,否则打印该行,然后在该边界内继续下一行。