Perl脚本在模式匹配之前和之后提取2行

时间:2011-05-08 13:06:38

标签: perl

我的文件就像

line 1 
line 2 
line 3
target
line 5
line 6
line 7

我可以写一个与目标匹配的正则表达式。我需要的是我需要抓住2,3,5,6行。 有没有办法做到这一点?

7 个答案:

答案 0 :(得分:6)

如果您不确定使用perl,则可以使用grep上下文线路控制选项轻松提取所需的上下文

grep -A 2 -B 2 target filename | grep -v target

当然target需要用合适的正则表达式替换。

答案 1 :(得分:4)

罗伯特正走在正确的道路上。您必须对正则表达式进行多行化并匹配前两行和下一行:

#!/usr/bin/perl -w

my $lines = <<EOF
line 1
line 2
line 3
target
line 5
line 6
line 7
EOF
;

# Match a new line, then 2 lines, then target, then 2 lines.
#                { $1       }        { $3       }
my $re = qr/^.*\n((.*?\n){2})target\n((.*?\n){2}).*$/m;

(my $res = $lines) =~ s/$re/$1$3/;
print $res;

答案 2 :(得分:2)

@lines = ('line 1', 'line 2', 'line 3', 'target', 'line 5', 'line 6', 'line 7');
my %answer;
$regex = 'target';
for my $idx (0..$#lines) {
    if ($lines[$idx] =~ /$regex/) {
        for $ii (($idx - 2)..($idx + 2)){
            unless ($lines[$ii] =~ /^$regex$/) {$answer{$ii} = $lines[$ii];}
        }
    }
}
foreach $key (sort keys %answer) { print "$answer{$key}\n" }

哪个收益......

[mpenning@Bucksnort ~]$ perl search.pl
line 2
line 3
line 5
line 6
[mpenning@Bucksnort ~]$

修改

修复了@ leonbloy关于文件中多个目标字符串的评论

答案 3 :(得分:2)

将文件粘贴到列表/数组中,找到匹配行的索引,并使用此索引获取所需的值(使用偏移量)

答案 4 :(得分:2)

尽管8个月前就提到了这个问题,但我不得不重新考虑这个问题,因为找不到任何解决方案都符合我的目标。我的目标是制作一个脚本,检查许多巨大的日志文件,并从中提取,只包含想要的行,在包含搜索模式的行之前和之后放置可选行数,没有任何冗余。我试图重用这里找到的一些代码,但对我来说这些代码都不够好。所以最后我创造了一个独特的,可能不是最美丽的,但看起来很有用,所以我想与你分享:

use strict;

my @findwhat      = ('x');
my $extraLines    = 3;
my @cache         = ('') x ($extraLines);
my @stack;
my $lncntr        = 0;
my $hit           = 0;
my $nextHitWatch  = 0;
my $shift         = 1;

open (IN, "<test1.log");
  while (my $line=<IN>) {
    $lncntr++;
    chomp $line;
    foreach my $what (@findwhat) {if ($line =~ m/$what/i) {$hit = 1; last}}

    if ($hit && !$nextHitWatch) {
      @stack = @cache;
      $hit = 0;
      $nextHitWatch++;
    }

    if (!$hit && $nextHitWatch && $nextHitWatch < ($extraLines * 2) + 2) {
      @stack = (@stack, $line);
      $nextHitWatch++;
    }

    if (!$hit && $nextHitWatch && $nextHitWatch == ($extraLines * 2) + 2) {
      @stack = (@stack, $line);
      for (my $i = 0; $i <= ($#stack - ($extraLines + $shift)); $i++) {
        print $stack[$i]. "\n" if $stack[$i];
      }
      $nextHitWatch = 0;
      $shift = 1;
      @stack = ();
    }

    if ($nextHitWatch >= 1 && eof) {
      foreach(@stack) {print "$_\n"}
    }

    if ($nextHitWatch >= 1 && eof) {
      if (!$hit) {
        my $upValue = 3 + $#stack - ($nextHitWatch - $extraLines + $shift);
        $upValue = ($upValue > $#stack) ? $#stack : $upValue;
        for (my $i = 0; $i <= $upValue; $i++) {
          print $stack[$i] . "\n";
        }
      } else {
        foreach (@stack) {print "$_\n"}
      }
    }

    shift(@cache);
    push(@cache, $line);
  }
close (IN);

可能您只需要更改列表@findwhat和标量$ extraLines的值。我希望我的代码可用。 (抱歉我的英语不好)

答案 5 :(得分:0)

多行正则表达式,例如:/\n{3}(foo)\n{3}/m;

修改 /\n*(foo)\n*/m适用于一般情况

答案 6 :(得分:0)

一个班轮版本(其中-l = chomp-n = while(<>){}。有关更多选项,请参阅perldoc perlrun

perl -lnE '$h{$.}=$_; END { 
  for ( grep { $h{$_} eq "target" } sort{ $a <=> $b } keys %h ) { 
  say for @h{$_-2..$_-1 , $_+1..$_+2} } }' data.txt

带解释的脚本:

#!perl
use feature 'say';

while (<DATA>) {
  chomp;
  $hash{$.} = $_  ; # hash entry with line number as key; line contents as value
}

# find the target in the hash and sort keys or line numbers into an array
@matches = sort {$a <=> $b} grep { $hash{$_} eq 'target' } keys %hash;

for (@matches) { 
  say "before\n" ;
  say for @hash{$_-2..$_-1} ; # print the context lines as a hash slice
  say ">>>>\" $hash{$.} \"<<<< " ;
  say "after\n" ;
  say for @hash{$_+1..$_+2} ;
  say "";
}

__DATA__
line 1
line 2
line 3
target
line 5
line 6
line 7
target
line of context1
line of context2
target

<强>输出

before
line 2
line 3
>>>>" target "<<<< 
after
line 5
line 6

before
line 6
line 7
>>>>" target "<<<< 
after
line of context1
line of context2

before
line of context1
line of context2
>>>>" target "<<<< 
after

一个更简单的版本,仅使用数组和输出,在请求的OP问题时排除目标:

#!perl -l     
chomp( my @lines = <DATA> ) ; 
my $n = 2 ; # context range before/after

my @indexes = grep { $lines[$_] =~ m/target/ } 0..$#lines ; 
foreach my $i (@indexes) { 
  print for @lines[$i-$n..$i-1], @lines[$i+1..$i+$n],"";
}

__DATA__
line 1
line 2
line 3
target
line 5
line 6
line 7
target
line of context1
line of context2
target

这可以避免构造散列,但在非常大的文件/数组上可能会更慢。

在CPAN List::MoreUtils上有indexes()且始终有splice(),但我不确定这会让事情变得更简单。