Show lines matching a pattern and the 4 lines before each

For example, from this file:

CREATE SYNONYM I801XS07 FOR I8010.I801XT07
               *
ERROR at line 1:
ORA-00955: name is already used by an existing object


CREATE SYNONYM I801XS07 FOR I8010.I801XT07
               *
ERROR at line 1:
ORA-00955: name is already used by an existing object



Table altered.


Table altered.


Table altered.


Table altered.


Table altered.


Table altered.


Table altered.


Table altered.

DROP INDEX I8011I01
           *
ERROR at line 1:
ORA-01418: specified index does not exist



Index created.

I want a way to find ORA- and show the ORA- line and the previous 4 lines:

CREATE SYNONYM I801XS07 FOR I8010.I801XT07
               *
ERROR at line 1:
ORA-00955: name is already used by an existing object

CREATE SYNONYM I801XS07 FOR I8010.I801XT07
               *
ERROR at line 1:
ORA-00955: name is already used by an existing object

DROP INDEX I8011I01
           *
ERROR at line 1:
ORA-01418: specified index does not exist

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The -B option to grep does exactly that: grep -B 4 ORA- your_file.

In the absence of GNU grep, I’ve adapted the grep4 example from the grymoire sed tutorial:

#!/bin/sh

# grepB4: prints out 4 lines before and the line including pattern
# if there is only one argument, exit

case $# in 
    1);;
    *) echo "Usage: $0 pattern";exit;;
esac;

sed -n '
'/"$1"/' !{
    # does not match - add this line to the hold space
    H
    # bring it back into the pattern space
    x
    # Two lines would look like .*n.*
    # Three lines look like .*n.*n.*
    # Delete extra lines - keep four
    s/^.*n(.*n.*n.*n.*)$/1/
    # put it back in hold space
    x
}
'/"$1"/' {
    # matches - append the current line
    H
    # bring hold space contents into pattern space
    g
    # print the 4 lines
    p
    # add the mark
    a
---
}'

Usage: grepB4 pattern < file.

Bruce Ediger’s answer does essentially the same thing with awk, which often has less cryptic syntax than sed.

Method 2

Supposing you’re on an elderly system, like HP-UX, that doesn’t have GNU utilities, just the old, original BSD or AT&T “grep”. You could do something like this:

#!/bin/sh

awk '/ORA-/ { print line1; print line2; print line3; print line4; print $0 }
// {line1 = line2; line2 = line3; line3 = line4; line4 = $0}' $1

Yes, there’s tons of edge conditions this doesn’t get right, but whatta ya want for nothing? Also, given that you’re working on some decroded, antiquated OS and hardware, you probably don’t have the CPU horsepower for fancy error handling.

Method 3

awk 'NR == FNR && $0 ~ p {
  for (i = FNR; i >= FNR - l; i--)
    nr[i]; next
  }
FNR in nr  
BEGIN {
  ARGV[ARGC++] = ARGV[ARGC - 1]
  }' l=4 p=ORA- infile

On Solaris use nawk or /usr/xpg4/bin/awk.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x