Kobi Orgad
Kobi Orgad

Reputation: 9

How to print 5 lines before and after the match regex with awk command

I want to use awk to print 5 lines before and 5 lines after the match regex (Start in ERROR and finish with on of INFO |WARN |DEBUG|TRACE). Also, I need to print the line number as well. I just success to print the line number

cat  foo |  awk '/\[ERROR\]/,/\[(INFO |WARN |DEBUG|TRACE)/{print NR":"$0}'

I don't care to find a solution with grep command

For example, the file contains:

DEBUG
DEBUG
DEBUG
TRACE
TRACE
INFO 
INFO 
ERROR
INFO 
INFO 
INFO 
DEBUG
DEBUG
DEBUG
DEBUG

The output should be:

3: DEBUG
4: TRACE
5: TRACE
6: INFO 
7: INFO 
**8: ERROR
9: INFO** 
10: INFO 
11: INFO 
12: DEBUG
13: DEBUG
14: DEBUG

The stars mark the match regex(Begin in ERROR, end in INFO)

Upvotes: 0

Views: 1979

Answers (3)

potong
potong

Reputation: 58371

This might work for you (GNU sed & nl):

nl -s': ' -w1 file |
sed -En ':a;N;s/\n/&/5;Ta
         /\n([0-9]+: ERROR[^\n]*)$/{
         s//\n**\1/;:b;p;n;/^[0-9]+: INFO/!bb;s/$/**/;:c;tc;N;s/\n/&/5;Tc;p;d};D'

Prepend line numbers using a : and space as a separator using the nl command and pipe the results into sed.

Fetch 5 lines into the pattern space and then check the sixth for the starting pattern.

If the sixth line does not contain the starting pattern, remove the first line, append another to the pattern space and repeat.

If the sixth line does contain the starting pattern, insert ** before the line number of that line, print/clear the pattern space, continue printing all lines until the ending pattern is matched.

Once the ending pattern is matched, prepend ** to end of that line, fetch and append 5 further lines and print the result.

The pattern space is cleared and the normal sed cycle is then resumed and the process repeated.

Upvotes: 0

Ed Morton
Ed Morton

Reputation: 203189

$ cat tst.awk
{ buf[NR%6] = $0 }
/ERROR/ { tgt=NR; f=1 }
tgt == NR {
    for (i=1; i<=6; i++) {
        print (NR+i-6) ":", buf[(NR+i)%6]
    }
}
f && /INFO|WARN|DEBUG|TRACE/ { tgt=NR+5; f=0 }

.

$ awk -f tst.awk file
3: DEBUG
4: TRACE
5: TRACE
6: INFO
7: INFO
8: ERROR
9: INFO
10: INFO
11: INFO
12: DEBUG
13: DEBUG
14: DEBUG

Upvotes: 3

James Brown
James Brown

Reputation: 37394

I submit this awk for peer reviewing, turned my brain to a bowl of spagetti (can't even spell spaghetti correctly):

$ awk '{
    $0=NR " " $0                     # prepend NR to record
}
!f {
    b[6]=$0                          # buffer to hold 5 previous lines
    for(i=1;i<=5;i++) {
        if(/ERROR/)                  # print previous buffer at ERROR
            print b[i]
        b[i]=b[i+1]
    }
}
/ERROR/,/(INFO|WARN|DEBUG|TRACE)/ {  # between markers arm the output flag
    f=5
}
f&&f--' file                         # keeps negative valued flags out

Upvotes: 0

Related Questions