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
  EVENT_TIME = 2018-03-06 12:01:23
  OUTPUT = 12


NODE1 2018-03-06 12:03:23
  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?

  • Hard way is pipe your commant to tail -n <number_of_lines_of_your_group>. – Yurij Goncharuk Mar 6 '18 at 21:17
  • @YurijGoncharuk problem is I don't know how many lines I need to capture. – Godzilla74 Mar 6 '18 at 21:18
  • You can use 'tac' instead of 'cat' and rewrite grep pattern for backward. – Yurij Goncharuk Mar 6 '18 at 21:22
  • If it's possible, put little sample of file in patebin.com. I will try to write pattern. – Yurij Goncharuk Mar 6 '18 at 21:24
  • @YurijGoncharuk pastebin.com/n2iDY8uY – Godzilla74 Mar 6 '18 at 21:36

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.

  • You're the man! This is way more consistent than the results I was getting to tac | .. | tac – Godzilla74 Mar 6 '18 at 22:33
  • One question... how can I amend this to only get the last 'TEST = POWER` command run? – Godzilla74 Mar 6 '18 at 22:35
  • Just a minute, I'm trying :) I have an idea. – Yurij Goncharuk Mar 6 '18 at 22:45
  • I've supplemented answer with your wishes ;) – Yurij Goncharuk Mar 6 '18 at 23:07

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


I find perl more convenient for this:

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


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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.