我正在搜索一些日志文件,其中有执行的操作组。每组的开头都有一行包含有关该组的信息,然后打印了有关每个操作的大量详细信息,并在每个单独的测试结束时打印了 PASS/FAIL 状态。
我想要做的是找到任何失败的操作并打印标题行,然后打印失败行之前的一些上下文。
例如:
Start test group ID 12345
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 PASSED
Start test group ID 238284
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 FAILED
以上内容可以浓缩为
Start test group ID 238284
verbose info
verbose info
Test 2 FAILED
FAILED 标志之前的行数对于每个测试来说并不是恒定的,并且每个测试的平均长度也不同,但是对于我来说,一个恒定的数字是可以接受的。无论如何,我通常只关心最后几行。
我觉得这对于 grep 来说可能有点复杂,但我从未真正使用过 awk 做任何事情并且不知道从哪里开始。
答案1
这里有一个awk
通过反向处理输出来简化事情的解决方案(需要命令tac
,它是 GNU coreutils 的一部分):
首先,awk
脚本(放在诸如“process.awk”之类的文件中)。对于一行bash
代码来说,它有点太长了。
BEGIN { output=0; any=0; }
/^Test .* FAILED/ { output=1; any=1; }
/^Test .* PASSED/ { output=0; }
/^Start test group/ && any == 1 { output=1; any=0; }
output == 1 { print; }
然后,在反转的日志文件上运行该脚本,并反转输出:
tac logfile | awk -f process.awk | tac
它是如何工作的?
首先,我们传递输入tac
以反转行的顺序(这样我们可以在读取“以下”行之前确定它们是否属于失败或通过的测试)。
脚本的工作原理如下。每个操作都包含一个必须匹配的条件,后面跟着一个代码块,如果当前行符合条件,则执行该代码块。
第一个动作是 BEGIN 动作,它总是在我们开始查看输入之前执行一次。它初始化了两个布尔标志,用于控制要打印的内容。output
如果我们要打印当前行,则设置为 1,否则设置为 0。any
每当我们遇到失败的测试时,都会设置为 1,在我们处理完一个测试组后,会重置为 0。两个值都从 0 开始。
下一个操作测试当前行,看它是否是失败测试的开头(记住,我们正在反向处理输出)。如果是,则同时设置output
和any
。
下一个操作测试当前行,看它是否是通过测试的开始。如果是,则清除标志output
,但不做any
任何处理。(在测试组结束之前可能仍有一个失败的测试)。
下一个操作测试当前行以查看它是否是测试组标题以及是否any
设置了标志。如果是,我们想要打印标题(我们至少有一个失败的测试),因此设置output
并清除any
(为下一个测试组做准备)。否则,我们不需要做任何事情;any
已经是 0,并且如果从未设置output
为,就不能设置为。1
any
最后,我们有一个操作,它不查看当前行,而只是检查先前的任何操作是否已设置output
。如果已设置,我们将打印当前行(可能是“测试失败”行、“在失败行之前”的一些详细信息或测试组标题)。
一旦所有操作都用尽,我们就会转到下一个输入行并尝试再次应用每个操作。在用尽所有输入后,我们将打印出我们想要的每一行输出,但顺序相反。通过管道传输输出可以解决这个问题tac
。
请注意,脚本可以变得更高效一些,但代价是使其更复杂,但它应该足够快。
答案2
脚本源自Bgs脚本在bash中:
buffer=""; cat /your/file | while read line
do
echo $line | grep -Eq "^Start" && start=$line && continue
echo $line | grep -q "FAILED" && echo -e "$start$buffer\n$line" \
&& buffer="" && continue
echo $line | grep -q "PASSED" && buffer="" || buffer="$buffer\n$line"
done
对于每个“FAILED”行,“Start”行以及“FAILED”行之前的所有行都会被打印,直到最后一个“FAILED”或“PASSED”行(不包括)。
输入文件示例:
Start test group ID 12345
verbose info #1
verbose info #2
Test 1 PASSED
verbose info #3
verbose info #4
Test 2 PASSED
verbose info #5
verbose info #6
Test 3 PASSED
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
verbose info #13
verbose info #14
Test 6 PASSED
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED
verbose info #18
verbose info #19
verbose info #20
Test 8 PASSED
脚本输出:
Start test group ID 12345
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
Start test group ID 98765
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED
答案3
思考一下……有几种方法可以实现这一点,
使用 AWK 捕获开始行,然后从最后一个 PASSED 或 FAILED 行开始捕获,直到获得 FAILED,此时您转储开始行和通向 FAILED 行的最后一个包
或者,使用 grep 分别过滤开始行和 FAILED 上下文并合并它们。为此,您需要保留行号。
尝试一下这个粗大王首先,
# script.awk
BEGIN {buffer1="";buffer2=""}
{
if ($1 == "Start")
{
buffer1=$0
}
else
{
if ($3 == "PASSED")
{
buffer2=""
}
else
{
buffer2=buffer2 "\n" $0;
if ($3 == "FAILED")
{
printf "%s%s\n",buffer1,buffer2
}
}
}
}
运行awk -f script.awk file.txt
笔记:
Start
如果您的、PASSED
或FAILED
线路不同, 则需要进行调整- 如果它们和你的例子一样一致,那么就很简单了
verbose
如果您的零件在“正确”的位置有上述 3 个“关键词”之一, 这也可能会失败。- 如果是这样,你需要添加更多背景信息来获取正确的关键词
- 这将获得所有失败的部分行
- 你可以稍微调整一下缓冲区以获取较少的数据
答案4
一个简单的 bash 解决方案(我将您的示例保存到 foo.txt 中):
buff="" ; cat foo.txt| while read line; do echo $line| grep -q "^Start" && buff="" ; buff="$buff\n$line" ; echo $line | grep -q FAILED && echo -e $buff; done