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