Perl-скрипт для извлечения 2 строк до и после сопоставления с шаблоном - PullRequest
5 голосов
/ 08 мая 2011

мой файл похож на

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

Я могу написать регулярное выражение, соответствующее цели. Все, что мне нужно, это мне нужно захватить строки 2,3,5,6. Есть ли способ сделать это?

Ответы [ 7 ]

6 голосов
/ 08 мая 2011

Если вы не решили использовать perl, вы можете легко извлечь нужный контекст с помощью grep и Контроля строки контекста опции

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

Конечно target нужно будет заменить подходящим регулярным выражением.

4 голосов
/ 08 мая 2011

Роберт на правильном пути.Вы должны умножить свое регулярное выражение и сопоставить 2 предыдущие и следующие строки:

#!/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 голосов
/ 12 января 2012

Хотя это было задано 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. Я надеюсь, что мой код будет полезен. (Извините за мой плохой английский)

2 голосов
/ 09 мая 2011

переводит файл в список / массив, находит индекс совпадающей строки и использует этот индекс для получения желаемых значений (с использованием смещений)

2 голосов
/ 08 мая 2011
@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 ~]$

EDIT

Исправлено для комментария @ leonbloy о нескольких целевых строках в файле

0 голосов
/ 14 октября 2014

Версия с одним вкладышем (где -l = chomp и -n = while(<>){}. См. perldocperlrun для дополнительных параметров):

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

Более простая версия, использующая только массивы и с выводом, исключающим цель в качестве запрашиваемого вопроса ОП:

#!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(), но я не уверен, что это упростит задачу.

0 голосов
/ 08 мая 2011

мультилинейное регулярное выражение, например: /\n{3}(foo)\n{3}/m;

редактировать /\n*(foo)\n*/m работает в общем случае

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...