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

Finding the last occurence in a given pattern

我有一个巨大的日志文件,其中有些行以 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),但没有得到预期的结果。

我不确定我是否完全理解你想要什么,下次展示你目前为止尝试过的东西,这有助于理解。不过,让我们尝试一下吧!

既然你想到了 perl 来做这件事,并且 perl 非常擅长处理文件和正则表达式,那么让我们使用 perl。

假设您将文件读入单个字符串 $string,您可以这样做:

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

解释:
.* 是一个 greedy 量词,它会尝试匹配最长的字符串。
.*? 与 .* 相同,但 nongreedy.
[^\n] 将匹配除 \n(换行符)以外的任何内容。
s 修饰符允许 . 匹配换行符。
因此,如果我用一句话解释正则表达式:找到最长的字符串,先于 Step,然后是尽可能少的字符,直到找到 Test done。 returns StepTest done 之间的所有文本,加上 末尾的所有文本测试完成 行。

您想要的结果将在 中。

既然您说日志文件是 "huge",那么将整个文件读入单个 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 开头的行,加上零个或多个 Step 或 [=15 开头的行=],加上以Test done:.

开头的行

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

保持简单,使用 awk:

$ awk '/^Step/{buf=""} {buf = buf [=10=] 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