跨多行的复杂匹配

时间:2014-09-11 04:14:24

标签: regex perl awk sed grep

一直在这里搜索并且接近但似乎仍然不是我正在尝试做的事情。例如。请考虑以下样本测试输入,目标是找到跨越包含“abc”(打印此行)的行的多行的匹配,并以包含“efg”(也打印此行)的行结束,并且还在两者之间打印线条。

yyabc}
000
iiabc<
    {efg+1}
111
yyabc}
222
 p  {efg+13}
zzz
   z   {efg+243} {}
iii
oooabc>
ooo

与我正在寻找的最接近的是,将zzz作为带有上述行的测试输入文件,

sed -e '/abc/,/efg/!d' zzz

,但包括额外的行,不介意不在那里,

yyabc}   <<***** extra
000      <<***** extra
iiabc<
    {efg+1}
yyabc}
222
 p  {efg+13}
oooabc>  <<***** extra
ooo      <<***** extra

,因此预期输出为,

iiabc<
    {efg+1}
yyabc}
222
 p  {efg+13}

除了依赖pcregrep(我在linux框中还有其他所有东西)之外,还有一个可以产生这样多行匹配的解决方案吗?

非常感谢。

6 个答案:

答案 0 :(得分:1)

sed -n '/abc/,/efg/ {
   H
   /efg/ {
      g
:a
      s/^.*\n\(.*abc\)/\1/
      ta
      p
      }
   }' zzz

使用缓冲区捕获abc和第一个efg之间的部分,而不是删除最后一个abc行之前的任何行,最后打印结果并继续文本的其余部分。

如果abc与efg位于同一行,并且之前没有来自&#34;相同的&#34;文本的一部分,因为sed //,//从一行上的patterne工作到另一行上的模式

答案 1 :(得分:1)

awk非常适合这项任务。如果测试输入文件名为zzz,则运行:

$ awk '/abc/{a=""} /abc/,/efg/{a=a"\n"$0} /efg/{print substr(a,2);a=""}' zzz
iiabc<
    {efg+1}
yyabc}
222
 p  {efg+13}

说明:

  • /abc/{a=""}

    每次到达包含“abc”的行时,将变量a设置为空字符串。 (我们要打印的行将在下一步中添加到此变量中。)

  • /abc/,/efg/{a=a"\n"$0}

    在以包含abc的行开头并以包含efg的行结尾的每一行范围内,每行都会附加到变量a

  • /efg/{print substr(a,2);a=""}

    当达到范围中的最后一行时,打印出a。由于a以额外的换行符开头,我们会使用substr将其删除。

如果没有上面的第一步,程序运行正常,但会打印“额外”行。包括第一步,它们就被淘汰了。

答案 2 :(得分:1)

使用perl one-liner打包整个文件:

perl -0777 -ne 'print /.*abc.*\n(?:(?!.*(?:abc|efg)).*\n)*.*efg.*\n/g' file.txt

或逐行缓冲解决方案:

perl -ne '
    $b = /abc/ ? $_ : "$b$_";
    print $b if (/abc/ .. /efg/) =~ /E/
  ' file.txt

切换

  • -0777:覆盖整个文件。
  • -n:为输入文件中的每个“行”创建一个while(<>){...}循环。
  • -e:告诉perl在命令行上执行代码。

答案 3 :(得分:1)

这可能适合你(GNU sed):

sed -n '/abc/,/efg/{/abc/{h;d};H;/efg/{g;p}}' file

通过调用-n开关在“grep”模式下使用sed。过滤abc和efg`之间感兴趣的行。使用保留空间(HS)存储包含线,然后将其打印出来。

替代:

sed -n '/abc/,/efg/{/abc/h;//!H;/efg/{x;p}}' file

答案 4 :(得分:0)

(.*?abc(?:(?:(?!efg|abc).)|\n)*efg.*$)

通过perl尝试这个。

参见演示。

http://regex101.com/r/bA0jG5/11

答案 5 :(得分:0)

基于阵列的直接awk解决方案:

awk '/abc/ {delete a;j=0;flag=1}
     flag    {a[++j]=$0}
     /efg/ && flag {for (i=1;i<=j;i++){print a[i]};flag=0}' inputfile

/abc/ {delete a;j=0;flag=1}:找到初始模式时,删除数组,将counter设置为零并打开&#34; find&#34;标志。

flag {a[++j]=$0}:标记开启时存储行内容。

/efg/ && flag {for (i=1;i<=j;i++){print a[i]};flag=0}:当找到结束模式并打开标志时,显示数组并关闭标志