Захват вывода с помощью Perl, пока не будет найден определенный шаблон - PullRequest
4 голосов
/ 18 февраля 2012

Мне кажется, что я здесь упускаю что-то очень простое, но это первый раз, когда мне нужно было это сделать, и я не могу найти пример.

У меня есть гигантская петля foreach, котораяпросматривает журналы вывода и извлекает различную информацию, основываясь на соответствующих регулярных выражениях.Моя главная проблема заключается в том, что несколько более крупных типов выходных данных имеют верхний и нижний колонтитулы, например *** Begin bangle tracking log***, за которыми следуют несколько строчек, а затем ***End bangle tracking log***.

Есть ли способ изнутри foreach loop, чтобы иметь внутренний цикл, в котором хранятся все строки, пока не будет найден нижний колонтитул?

foreach my $line( @parseme )
{
    if( $line =~ m/***Begin bangle tracking log***/ )
    {
        #Help! Push all lines into an array until bangle tracking footer is found.
    }
    if( $line =~ m/Other stuff I am tracking/ )
    {
        #Do other things
    }
}

Ответы [ 4 ]

5 голосов
/ 18 февраля 2012

Вы можете использовать оператор диапазона , который действует как триггер в скалярном контексте:

foreach ( @parseme ) {
    if ( /Begin bangle tracking log/ .. /End bangle tracking log/ ) {
        push @array, $_;
    }
    # other stuff...
}

Я использовал $_ для цикла foreach, потому что он обеспечивает более краткий синтаксис. Вы можете использовать другую переменную, если хотите, но тогда вам нужно записать условие в виде:

if ( $line =~ /Begin .../ .. $line =~ /End .../ ) {

, который может быть более читабельным с некоторыми дополнительными скобками:

if ( ($line =~ /Begin .../) .. ($line =~ /End .../) ) {

Одна проблема, которую следует отметить в связи с оператором триггера, заключается в том, что он запоминает свое состояние даже после окончания цикла. Это означает, что, если вы намереваетесь запустить цикл снова, вы действительно должны убедиться, что массив @parseme заканчивается строкой, соответствующей регулярному выражению /End .../, так что триггер будет быть в известном состоянии, когда цикл запускается в следующий раз.

Редактировать: Согласно приведенному ниже комментарию DVK, если вы хотите обработать собранные строки, как только достигнете строки нижнего колонтитула, вы можете сделать это, проверив возвращаемое значение оператора .., который заканчивается на E0 в последней строке:

foreach ( @parseme ) {
    my $in_block = /Begin bangle tracking log/ .. /End bangle tracking log/;
    if ( $in_block ) {
        push @array, $_;
    }
    if ( $in_block =~ /E0$/ ) {  # last line
        # process the lines in @array
        @array = ();
    }
    # other stuff...
}
4 голосов
/ 18 февраля 2012

Вы можете сделать это несколько проще, внедрив примитивный конечный автомат:

my $inside_bangle = 0; # 0=outside block, 1=inside
my @buffer;
foreach my $line( @parseme ) {
    if  ($line =~ m/***Begin bangle tracking log***/ ) {
        $inside_bangle = 1;
        next;
    }
    if ($line =~ m/***End bangle tracking log***/ ) {
        $inside_bangle = 0;
        # PROCESS @buffer somehow
        next;
    }
    if ($inside_bangle) {
        push @buffer, $line;
        next;
    }
    if ($line =~ m/other stuff i am tracking/ ) {
        #Do other things
    }
}

Другой вариант - использовать триггер (..)

1 голос
/ 18 февраля 2012

Возможно, вы ищете оператор .., который обладает некоторыми магическими свойствами при применении с регулярными выражениями.Следующий пример украден из PLEAC :

while (<>) {
    if (/BEGIN PATTERN/ .. /END PATTERN/) {
        # line falls between BEGIN and END in the
        # text, inclusive.
    }
}

Внутри блока добавьте переменную массива, как считаете нужным.

0 голосов
/ 18 февраля 2012

И теперь вы можете хранить несколько экземпляров своего фрагмента журнала, если это происходит более одного раза (оригинальный код DVK):

my $inside_bangle = 0; # 0=outside block, 1=inside
my %buffer;
my $index = 0;
foreach my $line( @parseme ) {
    if  ($line =~ m/***Begin bangle tracking log***/ ) {
        $inside_bangle = 1;
        next;
    }
    if ($line =~ m/***End bangle tracking log***/ ) {
        $inside_bangle = 0;
        $index++;
        # PROCESS @buffer somehow
        next;
    }
    if ($inside_bangle) {
        push @{ $buffer{$index} }, $line;
        next;
    }
    if ($line =~ m/other stuff i am tracking/ ) {
        #Do other things
    }
}

Вот что я написал изначально, но я думал, что код DVK более читабелен и аккуратен:

open FILE, "<", 'testfile.log';
@parseme = <FILE>;
my $initialize = shift @parseme;
my $startLogging = $initialize =~ m/^Log Start$/ ? 1 : 0; # test if first line of array is start of log
my %storage = ();
my $index = 0;
foreach my $line (@parseme) {
 $startLogging = 1 if $line =~ m/^Log Start$/;
 if ($startLogging == 1) {
  push( @{ $storage{$index} }, $line ) unless $line =~ m/(Log Start|Log End)$/;
  if ($line =~ m/^Log End$/) {
   $startLogging = 0;
   $index++;
  }
 }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...