查找给定模式中的最后一次出现
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 Step 和 Test 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
我有一个巨大的日志文件,其中有些行以 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 Step 和 Test 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