【问题标题】:Grepping second pattern after matching first pattern在匹配第一个模式后 Grepping 第二个模式
【发布时间】:2015-08-08 03:30:19
【问题描述】:

是否有任何 grep/sed 选项可以让我在匹配另一个模式后匹配一个模式?例如:输入文件(foos 是变量模式,以0 开头,前面是# 前面的随机数):

0foo1  
0foo2  
0foo3  
\#89888  
0foo4  
0foo5  
\#98980  
 0foo6

所以一旦我尝试搜索变量模式(例如foo2),我还想从该模式行号匹配另一个模式(例如#number),在本例中为#89888

因此变量foo2 的输出必须是:

foo2  #89888

对于变量foo5

foo5  #98980

foos 由每个字符组成,包括可能被视为元字符的字符。

我使用 tcl 尝试了一个基本的正则表达式匹配脚本,它将首先搜索foo*,然后搜索下一个直接#,但由于我正在处理一个非常大的文件,因此需要几天时间才能完成。任何帮助表示赞赏。

【问题讨论】:

  • 你能改用pcregrep吗,它支持多行?

标签: regex perl sed grep tcl


【解决方案1】:
#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

my ( %matches, $recent_foo );

while(<DATA>)
{
   chomp;
   ( $matches{$recent_foo} ) = $1 if m/(\\#\d+)/;
   ( $recent_foo ) = $1 if m/(0foo\d+)/;
}

print Dumper( \%matches );

__DATA__
0foo1  
0foo2  
0foo3  
\#89888  
0foo4  
0foo5  
\#98980  
 0foo6

 ./perl 
$VAR1 = {
          '0foo5' => '\\#98980',
          '0foo3' => '\\#89888'
        };

【讨论】:

    【解决方案2】:

    一个 Perl 单行器,用于 slurp 整个文件并匹配您寻找的模式的任何换行符,如下所示:

    perl -000  -nle 'm{(foo2).*(\#89888)}s and print join " ",$1,$2' file
    

    -000 开关启用“slurp”模式,该模式指示 Perl 不要将文件拆分为块,而是将其视为一个大字符串。 s 修饰符让. 匹配任何字符,包括换行符。

    【讨论】:

      【解决方案3】:

      如果您想要的是 0foo10foo20foo3,它们都具有相同的值,则可以:

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      use Data::Dumper;
      
      my ( %matches, @recent_foo );
      
      while(<DATA>)
      {
         chomp;
         if (/^\\#/)
         {
           @matches{@recent_foo} = ($') x @recent_foo;
           undef @recent_foo;
         }
         elsif (/^0/)
         {
           push @recent_foo, $';
         }
      }
      
      print Dumper( \%matches );
      
      __DATA__
      0foo1
      0foo2
      0foo3
      \#89888
      0foo4
      0foo5
      \#98980
      0foo6
      

      给予:

      $VAR1 = {
                'foo2' => '89888',
                'foo1' => '89888',
                'foo5' => '98980',
                'foo3' => '89888',
                'foo4' => '98980'
              };
      

      【讨论】:

        【解决方案4】:
        Var='foo2'
        sed "#n
        /${Var}/,/#[0-9]\{1,\}/ {
           H
           /#[0-9]\{1,\}/ !d
           s/.*//;x
           s/.//;s/\n.*\\n/ /p
           q
           }" YourFile
        

        要求不明确。它首先出现您的模式 foo2 直到第一个 #number,删除之间的行并在 1 中打印两行而不是退出(没有其他提取

        【讨论】:

          【解决方案5】:

          Tcl 解决方案。该过程在 3 微秒多一点的时间内运行,因此您需要 非常 个大型数据文件才能运行数天。如果多个标记匹配,则使用第一个匹配(很容易重写该过程以返回所有匹配项)。

          set data {
          0foo1  
          0foo2  
          0foo3  
          \#89888  
          0foo4  
          0foo5  
          \#98980  
           0foo6
          }
          
          proc find {data pattern} {
              set idx [lsearch -regexp $data $pattern]
              if {$idx >= 0} {
                  lrange $data $idx $idx+1
              }
          }
          
          find $data 0foo3
          # -> 0foo3 #89888
          find $data 0f.*5
          # -> 0foo5 #98980
          

          文档:iflrangelsearchprocset

          【讨论】:

            【解决方案6】:

            sed

            sed -n '/foo2/,/#[0-9]\+/ {s/^[[:space:]]*[0\\]//; p}' file | 
            sed -n '1p; $p' | 
            paste -s
            
            • 第一个 sed 打印第一个模式和第二个模式之间的所有行,删除可选的前导空格和前导 0\
            • 第二个 sed 只提取第一行和最后一行。
            • 粘贴命令将 2 行打印为单行,用制表符分隔。

            awk

            awk -v p1=foo5 '
                $0 ~ p1 {found = 1} 
                found && /#[0-9]+/ { sub(/^\\\/, ""); print p1, $0; exit }
            ' file
            

            tcl

            lassign $argv filename pattern1
            set found false
            set fid [open $filename r]
            while {[gets $fid line] != -1} {
                if {[string match "*$pattern1*" $line]} {
                    set found true
                }
                if {$found && [regexp {#\d+} $line number]} {
                    puts "$pattern1 $number"
                    break
                }
            }
            close $fid
            

            然后

            $ tclsh 2patt.tcl file foo4
            foo4 #98980
            

            【讨论】:

              【解决方案7】:

              这是你想要的吗?

              $ awk -v tgt="foo2" 'index($0,tgt){f=1} f&&/#[0-9]/{print tgt, $0; exit}' file
              foo2 \#89888
              
              $ awk -v tgt="foo5" 'index($0,tgt){f=1} f&&/#[0-9]/{print tgt, $0; exit}' file
              foo5 \#98980
              

              我在上面使用index(),因为它搜索的是字符串而不是正则表达式,因此不太关心 foo 中的 RE 元字符 - 它们都只是字符串中的文字字符。

              您的问题不清楚您是想在特定 foo 之后查找特定数字还是 foo2 之后的第一个数字,或者即使您想搜索特定 foo 值或所有“foo”或...

              【讨论】:

                猜你喜欢
                • 2020-02-09
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-04-03
                相关资源
                最近更新 更多