Link to home
Start Free TrialLog in
Avatar of shanetexas
shanetexas

asked on

Using sed to match a pattern and then return the matched line as well as select lines above it.

Hello, I would like to use sed to search a file for a pattern (word) and then return the line containing that word, as well as the previous 3 lines.

For example, below is my sample file:

Apples are red
Oranges are orange
Pears are green
Bananas are yellow
Turnips are purple
Potatoes are brown

I would like to now search for the work Turnips and the command return the line containing Turnips as well as the previous 3 lines.

So, my output would look like below:

Oranges are orange
Pears are green
Bananas are yellow
Turnips are purple

I have searched sed websites and the closest I can come to the commanded needed is below. But, the below command excludes the line containing the match and I want the command to not only print the previous 4 lines, but also print the line containing the pattern.

#Print the line immediately after a regexp, but not the line
#Containing the regexp
 sed -n '/regexp/{n;p;}'

1.     So I created a sample file (below):

ibm05:/home/root (707)#cat sample_file    
Apples are red
Oranges are orange
Pears are green
Bananas are yellow
Turnips are purple
Potatoes are brown

2.     Then I modified the above command and here is what I get:

ibm05:/home/root (710)#sed -n '/Bananas/{g;1!p;};h' ./sample_file

ibm05:/home/root (711)#

All I get is a blank line. Any help that can be provided will be much appreciated, thanks.
Avatar of HamdyHassan
HamdyHassan

is this should be in sed ?
Is this homework ?
Avatar of shanetexas

ASKER

No. I'm trying to create a report for my work out of a large output file. Is there another was to accomplish this other than sed?
yes using perl script

---------------------
source code
---------------------

#!/usr/bin/perl

#----------------------
# Define input file
#----------------------
$sourceFile="ss.txt";

#---------------------------------------------------
# Define output files
#---------------------------------------------------
$DATAFile="ss.out";

#----------------------
# Create File Handlers
#----------------------
open(IN,"$sourceFile");
open(OUTA,">$DATAFile");

$l1="";
$l2="";
$l3="";

while ($myline=<IN>){
   if ($myline =~ /Potatoes/)
   {
     print OUTA $l3 ;
     print OUTA $l2 ;
     print OUTA $l1 ;
     print OUTA $myline ;
   }
   $l3=$l2;
   $l2=$l1;
   $l1=$myline;
}
close(IN);
close(OUTA);


-------------
input file
-------------
2733 bin/ee> cat ss.txt
Apples are red
Oranges are orange
Pears are green
Bananas are yellow
Turnips are purple
Potatoes are brown


-------------
output file
-------------
2734 bin/ee> cat ss.out
Pears are green
Bananas are yellow
Turnips are purple
Potatoes are brown
Is there a less complex way with sed?
It should be easy also with AWK
How would I do it with awk?
Trivially in awk ...

awk -v pattern="$pattern" 'BEGIN {
            prev1 = prev2 = prev3 = "";
}
{ if ($0 ~ pattern) print prev1 "\n" prev2 "\n" prev3 "\n" $0;
}
{ prev1 = prev2 ; prev2 = prev3 ; prev3 = $0 }
}' file

There are more elegant methods, but this should suffice, although it might output some spurious blank lines.
The awk script provided did not work. Here is the output it produced (I put the awk script into a file names shane2):

tech2:/home/root (695)#shane2          



Apples are red


Apples are red
Oranges are orange

Apples are red
Oranges are orange
Pears are green
Apples are red
Oranges are orange
Pears are green
Bananas are yellow
Oranges are orange
Pears are green
Bananas are yellow
Turnips are purple
Pears are green
Bananas are yellow
Turnips are purple
Potatoes are brown
Avatar of Tintin
Depends what flavour Unix you have or if you have GNU grep.

If so, then it is simple:

grep -B 3 Turnips sample.txt
ASKER CERTIFIED SOLUTION
Avatar of ecw
ecw

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
whoops, line shouldn't be
 awk -v pattern="$1"
should be
 pattern="$1"
Well if the file size isnt too big, then you can try this

#!/usr/bin/perl

$filehandle = "/home/somewhere/some.txt";
open ( FILE, "$filehandle");
@lines =reverse <FILE>;
for $i ( 0 .. $#lines ) {
   if ( $lines[$i] =~ /Turnip/g) {
      @array_line = @lines[$i,$i+1,$i+2,$i+3];
      print "@array_line";
   }
}
exit 0;

No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

Answered by ecw

Please leave any comments here within the next seven days.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

liddler
EE Cleanup Volunteer