20

I am trying to print the matched line and the 4th line from the matched line (line containing the expression I am searching for).

I have been using the following code: sed -n 's/^[ \t]*//; /img class=\"devil_icon/,4p' input.txt

But this only prints the matched line.

This prints only the 4th line. awk 'c&&!--c;/img class=\"devil_icon/{c=4}' input.txt

I need to print both the matched line and the 4th line only.

4
  • Use egrep "pattern" -A4 Aug 24, 2013 at 10:52
  • @val0x00ff that prints the lines in between too.. that is: it prints next 4 lines starting from the matched line
    – debal
    Aug 24, 2013 at 10:56
  • you are saying "I am trying to print the matched line and the 4th line from the matched line". This grep -A 4 "pattern" file | sed -n '4p' does do exactly what you want, unless I'm misunderstanding you Aug 24, 2013 at 11:32
  • no it doesn't. The output of the above code was </td> which is not the 4th line
    – debal
    Aug 24, 2013 at 11:42

5 Answers 5

24

In awk, you'd do it as follows

awk '/pattern/{nr[NR]; nr[NR+4]}; NR in nr' file > new_file`

or

awk '/pattern/{print; nr[NR+4]; next}; NR in nr' file > new_file`

Explanation

The first solution finds all lines that match pattern. When it finds a match it stores the record number (NR) in the array nr. It also stores the 4th record from NR in the same array. This is done by the nr[NR+4]. Every record (NR) is then checked to see if it's present in the nr array, if so the record is printed.

The second solution works essentially the same way, except when it encounters th e pattern it prints that line, and then stores the 4th record ahead of it in the array nr, then goes to the next record. Then when awk encounters this 4th record the NR in nr block will get executed and print this +4 record there after.

Example

Here's an example data file, sample.txt.

$ cat sample.txt 
1
2
3
4 blah
5
6
7
8
9
10 blah
11
12
13
14
15
16

Using the 1st solution:

$ awk '/blah/{nr[NR]; nr[NR+4]}; NR in nr' sample.txt 
4 blah
8
10 blah
14

Using the 2nd solution:

$ awk '/blah/{print; nr[NR+4]; next}; NR in nr' sample.txt 
4 blah
8
10 blah
14
5
  • 4
    Nice, +1. You're using a lot of awk shortcuts here, could you add a short explanation (things like print being implied in awk and that arrays are associative etc)?
    – terdon
    Aug 24, 2013 at 14:16
  • a agree with @terdon please could you explain the code a little.
    – debal
    Aug 24, 2013 at 14:17
  • @slm Thanks for improving and providing the complete answer! Aug 24, 2013 at 15:45
  • 1
    Thanks for the answer, I learned somthing new with it too.
    – slm
    Aug 24, 2013 at 16:06
  • 1
    It's also helpful to understand that the NR in nr part basically implies this: if (NR in n) print $0 - which is performed for each line and matches when the current NR equals the previously stored line number(s) .i.e. NR+4.
    – Pierz
    Jan 17, 2022 at 14:20
5

You can try the -A option with grep, which specifies how many lines after the matching line should be printed. Couple this with sed, and you would get the required lines.

grep -A 4 pattern input.txt | sed -e '2,4d'

Using sed, we delete the from the second line until the fourth.

1
  • 3
    This assumes a single match of pattern in the file.
    – terdon
    Aug 24, 2013 at 14:13
4
sed -n 's/^[ \t]*/; /img class=\"devil_icon/,+4 { 3,5d ; p }' input.txt

I'm simply adding a deletion of the appropriate lines, before printing { 3,5d ; p }.

1
  • your expression produces an error: sed: -e expression #1, char 18: unknown option to s'`
    – minerals
    Jan 31, 2017 at 10:42
2

Here's a way in Perl which can deal with an arbitrary number of matching lines:

perl -ne '/pattern/ && do{$c=$.; print}; $.==$c+4 && print' file > new_file`

In Perl. the special variable $. is the current line number. So, each time I find a line matching pattern, I print it and save its line number as $c. I then print again when the current line number is 4 more than the one printed previously.

0
awk 'c&&!--c;/img class=\"devil_icon/{c=4};/img class=\"devil_icon/' input.txt

You're essentially doing a find and replace. You can add just a find into the same command and it'll print both of them :)

awk 'c&&!--c;/pattern/{c=4};/pattern/' input.txt

You must log in to answer this question.

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