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?

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

3 Answers 3

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.

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

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .