找到给定模式中的最后一次出现

时间:2016-02-21 17:09:09

标签: regex regex-lookarounds

我有一个巨大的日志文件,其中一些行以Step开头,有些行以Test done:开头,有些以两者开头。

对于每个Test done:行,我想提取自上一行Step行以来的所有内容。

因此,例如,如果这是日志文件:

Step 1: Do Blah
Value of blah is 1
Step 2: Do blah blah
Value of blah blah is 2
Step 3: "foo bar baz"
Value of baz is 3
Test done: dummy1 failed
Step 4 :Verify, blah blah blah
NODE-1# ls -l
testcase failed
Test done: dummy_2 failed

然后我想提取这个:

Step 3: "foo bar baz"
Value of baz is 3
Test done: dummy1 failed

和此:

Step 4 :Verify, blah blah blah
NODE-1# ls -l
testcase failed
Test done: dummy_2 failed

我尝试了Perl正则表达式(Step(?!Step.*).*?Test done),但它没有得到预期的结果。

3 个答案:

答案 0 :(得分:2)

我不确定我是否完全理解你想要的东西,使用你下次到目前为止尝试过的东西,它有助于理解。但是,让我们尝试一下!

既然你认为perl会这样做,并且perl非常擅长使用文件和正则表达式,那么让我们使用perl。

假设您将文件读入单个字符串$string,您可以执行以下操作:

$string =~ m/.*(Step.*?Test done[^\n]*)/s

解释:
.*贪心量词,它会尝试匹配最长的字符串。
.*?与。*相同,但 nongreedy [^\n](换行符)外,\n将匹配任何内容 s修饰符允许.匹配换行符 因此,如果我用一个句子解释正则表达式:找到 Step 之前的最长字符串,然后找到可能的字符,直到找到 Test done 。并返回 Step Test done 之间的所有文本,以及 Test done 行末尾的所有文本。

您想要的结果将在$1

答案 1 :(得分:0)

由于您说日志文件是“巨大的”,因此将整个内容读入单个Perl字符串可能不是一个好主意。

相反,我建议迭代这些行,并跟踪自最新Step行以来的所有行:

my @lines_to_print = ();
while (<>) {
    if (m/^Step/) {
        @lines_to_print = ();
    }
    push @lines_to_print, $_;
    if (m/^Test done:/) {
        print @lines_to_print;
    }
}

但如果您仍然喜欢正则表达式方法,可以将文件读入字符串,然后写:

m/^Step.*\n(?>(?:(?!Step|Test done:).*\n)*)Test done:.*\n/gm;

捕获任何以Step开头的行,加上零行或多行 not StepTest done:开头,加上以Test done:开头的行。

请注意,在上述两种方法中,如果第一个Test done:行位于第一个Step行之前,或者如果有两个Test done:,我就不会担心该行为他们之间没有Step行的行,因为这似乎不可能?如果 可能,并且如果该情况下的行为很重要,请告诉我,我可以调整以上内容。

答案 2 :(得分:0)

保持简单,只需使用awk:

$ awk '/^Step/{buf=""} {buf = buf $0 ORS} /^Test done/{print buf}' file      
Step 3: "foo bar baz"
Value of baz is 3
Test done: dummy1 failed

Step 4 :Verify, blah blah blah
NODE-1# ls -l
testcase failed
Test done: dummy_2 failed