Monday, August 13, 2012

7 examples to print few lines before a pattern match in Linux



In this article, we will discuss the different ways for how to print a few lines before a pattern match in log files. You will do well to know how to print a few lines AFTER a pattern match.

 Let us consider a sample file as below. The requirement is to print 2 lines before the pattern 'Linux':
$ cat file
Unix
AIX
Solaris
Linux
SCO
1. grep will do it for you if your grep is a GNU grep.
$ grep -B2 Linux file
AIX
Solaris
Linux
The option '-B' will give n lines before the pattern. Hence '-B2 Linux' gives 2 lines before the pattern 'Linux' including the line matching the pattern.

2. awk way:
$ awk '/Linux/{for(i=1;i<=x;)print a[i++];print}
>             {for(i=1;i<x;i++)a[i]=a[i+1];a[x]=$0;}'  x=2 file
AIX
Solaris
Linux
 In this, an array is used which will always contain 2 lines before the current pattern. Hence, when the pattern is matched, the array contents along with the current pattern is printed.

3. Perl way:
$ perl -ne 'push @arr,$_; shift @arr if(@arr>3);
>          /Linux/ and print @arr;' file
AIX
Solaris
Linux
On reading a line, the line is pushed into an array named "arr". As and when the array size goes beyond 3, an element is removed from the front. In this way, the array will always contain 3 lines including the current pattern. On encountering the pattern 'Linux', the array is printed.

4. sed and tail combination:
$ sed -n '1,/Linux/p' file | tail -3
AIX
Solaris
Linux
Using sed, a range of lines can be read. sed reads from 1st line till the pattern 'Linux'. From this result, we print the last 3 lines which is nothing but the line containing the pattern and 2 lines before the pattern.

5. awk and sed combination:
$ x=`awk '/Linux/{print NR-2","NR}' file`
$ sed -n "$x p" file
AIX
Solaris
Linux
  Using awk, the line number range is retrieved using the pattern 'Linux'. NR gives the current line number which is the number of the line containing the pattern, NR-2 gives the gives the number of the line which is 2 lines before. Using this line range in sed, the set of lines can be filtered.

6. Another awk way, only if the file is a small one:
$ awk '{a[++i]=$0;}/Linux/{for(j=NR-2;j<=NR;j++)print a[j];}' file
AIX
Solaris
Linux
 In this, awk stores the entire file in memory using an array. On encountering the pattern 'Linux', it starts printing from 2 lines before using the for loop. Since awk stores the entire file in memory, this SHOULD not be used in case of large files since it will cause performance issues.

7. tac command (The least favored option)
$ tac file | grep -A2 Linux | tac
AIX
Solaris
Linux
tac command is reverse of cat command which prints the file in reverse order. On the reversed file, grep for 2 lines above the pattern, and again reverse the output and display. This is just an option, not the preferred one due to its performance. tac command itself is not good for performance, add to that, using it twice.
 Note: tac command is not available in all flavors of Unix.

1 comment:

  1. Quite useful. On grep you can also use -Context to print lines around matching. I have discussed this on my post 10 example of grep command in Unix you may find useful.

    Javin

    ReplyDelete