Here's an awk
solution that simplifies things by processing the output in reverse (requires the tac
command, which is part of the GNU coreutils):
First, the awk
script (put in a file such as 'process.awk'). It's just a little too long for a bash
one-liner.
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; }
Then, run that script on the reversed log file, and reverse the output:
tac logfile | awk -f process.awk | tac
How does it work?
First, we pass our input through tac
in order to reverse the order of the lines (so we can determine if the "following" lines belong to a FAILED or PASSED test before reading them).
The script works as follows. Each action consists of a condition that must be matched, followed by a block of code to execute if the current line matches the condition.
The first action is a BEGIN action, which is always executed once before we start looking at the input. It initialized two boolean flags that control what gets printed. output
will be set to 1 if we want to print the current line, 0 otherwise. any
will be set to 1 any time we encounter a FAILED test, and reset to 0 after we've finished processing a test group. Both values start at 0.
The next action tests the current line to see if it is the beginning of a failed test (remember, we're processing the output in reverse). If so, set both output
and any
.
The next action tests the current line to see if it is the beginning of a passed test. If so, clear the output
flag, but leave any
alone. (There might still be a failed test before the end of the test group).
The next action tests the current line to see if it is the test group header and if the any
flag is set. If it is, we want to print the header (we had at least one failed test), so set output
and clear any
(to prepare for the next test group). Otherwise, we don't need to do anything; any
is already 0, and output
can't have been set to 1
if any
never was.
Finally, we have an action that doesn't look at the current line, but just checks if any of the previous actions have set output
. If they have, we print the current line (which may be a "Test FAILED" line, some verbose info that "precedes" the FAILED line, or a test group header).
Once all the actions are exhausted, we move to the next input line and try to apply each action again. After all the input is exhausted, we'll have printed each of the output lines we want, but in reverse order. Piping the output through tac
fixes that.
Note that script could be made a bit more efficient at the cost of making it more complex, but it should be fast enough.
+1, compact AWK with a long description and nice trick with
tac
for easier parse. – nik – 2012-06-14T17:57:02.560