1

I'm running the following command against a log file and only wanting to get the last/latest match. Sometimes there may only be one match, other times there may be multiple, which is causing an issue for me since the following command is returning both matches:

cat "$(ls -t | head -n1)" | grep -P "(NODE1[\s\S]*TEST\s=\sPOWER[\s\S]*OUTPUT\s=\s\d+?.*\s+;?)"

>>>>> (results in)...

NODE1 2018-03-06 12:01:23
  TEST = POWER
  EVENT_TIME = 2018-03-06 12:01:23
  OUTPUT = 12

;

NODE1 2018-03-06 12:03:23
  TEST = POWER
  EVENT_TIME = 2018-03-06 12:03:23
  OUTPUT = 7

;

I need the last matching group in the event there are multiple. Is this possible with grep/regex or do I need to pipe the results into sed/awk? If so, how?

Godzilla74
  • 135
  • 1
  • 7

3 Answers3

2

Based on Yurij's suggestion, I started looking into using tac instead of cat, and reversing my grep statement. Now, I'm looking at the file from the bottom up and grabbing the first match:

tac "$(ls -t | head -n1)" | grep -m 1 -P "\d+[\s\S]*TEST\s=\sTXPOWER" | tac

Godzilla74
  • 135
  • 1
  • 7
1

I propose to you this solution:

cat <your_source_file> | sed -n '/NODE1/,/;/p' | tr '\n' '|' | awk -F ';' '{print $(NF-1)}'|tr '|' '\n'

sed -n '/NODE1/,/;/p' - find 'NODE1' blocks.

tr '\n' '|' convert newlines to record separator, so table columns will be separated by ';'.

awk -F ';' '{print $(NF-1)}' - print last-1 table column.

tr '|' '\n' - backward to previous view for record.

awk -F ';' '{for(i=(NF-1); i>0; i--){ if($i ~ "TEST = POWER"){print $i} } }' - only "TEST = POWER" events.

Yurij Goncharuk
  • 217
  • 1
  • 2
  • 13
0

I find perl more convenient for this:

perl -lane 'if(/^NODE1 /&&($#n=-1)../^;$/){push @n,$_} END{print $_ for @n}' file

Explanation:

perl Practical Extraction and Reporting Language.
-lane switches commonly helpful for one-liners.
' start of the actual program instructions
if(/^NODE1 /&&($#n=-1)../^;$/) consider only portions of text beginning with a line stat starts with NODE1 followed by a whitespace, and ending with a line containing a single semicolon.
/^NODE1 /&&($#n=-1) reset the @n array at the start of the text portion.
{push @n,$_} save each line in the text portion as an element of an array called @n
END{print $_ for @n} when the whole file has been parsed, print the text portion saved in @n.
' end of the program instructions.
file this is a placeholder for the name of the file you wish to process.

simlev
  • 1,105
  • 3
  • 14
  • 22