用于获取特定先前上下文的 Bash 脚本

用于获取特定先前上下文的 Bash 脚本

我正在搜索一些日志文件,其中有执行的操作组。每组的开头都有一行包含有关该组的信息,然后打印了有关每个操作的大量详细信息,并在每个单独的测试结束时打印了 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 开始。

下一个操作测试当前行,看它是否是失败测试的开头(记住,我们正在反向处理输出)。如果是,则同时设置outputany

下一个操作测试当前行,看它是否是通过测试的开始。如果是,则清除标志output,但不做any任何处理。(在测试组结束之前可能仍有一个失败的测试)。

下一个操作测试当前行以查看它是否是测试组标题以及是否any设置了标志。如果是,我们想要打印标题(我们至少有一个失败的测试),因此设置output并清除any(为下一个测试组做准备)。否则,我们不需要做任何事情;any已经是 0,并且如果从未设置output为,就不能设置为。1any

最后,我们有一个操作,它不查看当前行,而只是检查先前的任何操作是否已设置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

思考一下……有几种方法可以实现这一点,

  1. 使用 AWK 捕获开始行,然后从最后一个 PASSED 或 FAILED 行开始捕获,直到获得 FAILED,此时您转储开始行和通向 FAILED 行的最后一个包

  2. 或者,使用 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

笔记:

  1. Start如果您的、PASSEDFAILED线路不同, 则需要进行调整
    • 如果它们和你的例子一样一致,那么就很简单了
  2. verbose如果您的零件在“正确”的位置有上述 3 个“关键词”之一, 这也可能会失败。
    • 如果是这样,你需要添加更多背景信息来获取正确的关键词
  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

相关内容