使用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,但是没有精确匹配,而且都不是高质量的,而且我相信这个特定的问题是最常见的问题,它应该有一个清晰的表述,以及一系列正确,清晰的答案。
答案 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
(假设PATTERN1
和PATTERN2
始终成对出现,并且其中一个都不成对出现)
$ 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,否则打印该行,然后在该边界内继续下一行。