【问题标题】:Complex matching across multiple lines跨多行的复杂匹配
【发布时间】:2014-09-11 15:43:05
【问题描述】:

一直在这里搜索并接近但似乎仍然不是我想要做的。例如。请考虑以下示例测试输入,目标是找到跨越多行的匹配,这些匹配以包含“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 盒子里有其他所有东西),有没有可以产生这样多行匹配的解决方案?

非常感谢。

【问题讨论】:

    标签: regex perl awk sed grep


    【解决方案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 将其删除。

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

    【讨论】:

    • 谢谢。是的,这也有效,并且通过使用文件大小为 2793383645 的相同测试输入确认可以正常工作。
    • 我喜欢这个答案,因为我能理解。谢谢大家!
    【解决方案2】:
    sed -n '/abc/,/efg/ {
       H
       /efg/ {
          g
    :a
          s/^.*\n\(.*abc\)/\1/
          ta
          p
          }
       }' zzz
    

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

    如果 abc 与 efg 位于同一行且文本的“相同”部分没有先前的 abc,则不起作用,因为 sed //,// 从一行上的模式工作直到另一行上的模式

    【讨论】:

    • 谢谢。试过了,它有效。并且还针对真实的测试输入进行了尝试,文件大小为 2793383645,并且也可以。
    【解决方案3】:

    使用 perl 单行器来吞咽整个文件:

    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(&lt;&gt;){...} 循环。
    • -e:告诉perl 在命令行上执行代码。

    【讨论】:

      【解决方案4】:

      这可能对你有用(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
      

      【讨论】:

        【解决方案5】:
        (.*?abc(?:(?:(?!efg|abc).)|\n)*efg.*$)
        

        通过 perl 试试这个。

        查看演示。

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

        【讨论】:

        • @dinan5m3 你得到了什么?
        • 该死的,这是怎么在stackoverflow中评论的??????谢谢。我试过了,得到了关注,$ cat zzz | perl -n000e 'print $&amp; while /(.*?abc(?:(?:(?!efg|abc).)|\n)*efg.*$)/gm'Close。修改为以下,似乎给出了预期的结果,$cat zzz | perl -n000e 'print $&amp; while /(.*?abc(?:(?:(?!efg|abc).)|\n)*efg.*$.*\n)/gm'
        • @dinan5m3 它回答了您的问题,不要忘记将其标记为正确。
        • 但是为什么这种 perl 方法在用于实际测试输入时不起作用,文件大小为 2793383645?但是使用 sed 的 @NeronLeVelu 解决方案,使用相同的 2793383645 字节测试输入是否按预期工作?
        • @dinan5m3 你得到什么错误......是因为回溯太多导致的内存问题吗?
        【解决方案6】:

        一个简单的基于数组的 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} : 找到初始模式时,删除数组,将计数器设置为零并打开“查找”标志。

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-20
          • 1970-01-01
          • 2012-01-27
          • 2019-10-07
          • 2014-06-19
          相关资源
          最近更新 更多