LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   print nth line after the line which matches the string (http://www.linuxquestions.org/questions/programming-9/print-nth-line-after-the-line-which-matches-the-string-910322/)

cristalp 10-26-2011 02:11 PM

print nth line after the line which matches the string
 
Dear Experts,

I have a file which looks like:
Code:

111111
222222
33aaa3
444444
555555
666666
777777
...

What I want is to print the third line (which should be 666666) after the line where aaa was found. I mean, I need to first search through the file to find a line includes string "aaa", then print out the third (or nth) line 666666 after this line.

I tired:
Code:

awk '{a[NR] = $0} $0 ~ /aaa/ {printf("%s\n", a[NR+3])}' FILENAME
This gave me empty output. But if I try for example:
Code:

awk '{a[NR] = $0} $0 ~ /aaa/ {printf("%s\n", a[NR-1])}' FILENAME
With this command, I can get the line 222222 which is above the line with aaa. (exactly NR-1).

So, why I can not do this same way for line after(NR+n)? How could I achieve my goal, with awk or grep(I am not that in sed)?

Any reply would be greatly appreciated. Thank you very much!

druuna 10-26-2011 02:51 PM

Hi,

Try this:
Code:

grep -A3 "aaa" infile | tail -1
Hope this helps.

grail 10-27-2011 12:28 AM

The issue with your awks is you can see in the past (ie what you have stored in the array), but you cannot see into the future (ie NR +3 has yet to be stored).

What you would try and do if you use awk is:
Code:

awk '/aaa/{x = NR + 3}NR == x' file

Nominal Animal 10-27-2011 01:37 AM

I'm with grail, except that if you have multiple matches per file, I'd use an indicator array:
Code:

awk '/aaa/ { show[NR+3] = 1 } (NR in show) { print $0 ; delete show[NR] }' infile
The delete will trim the array, so it'll ever only contain the to-be-printed record numbers. It's a good idea if you happen to process files with many occurrences, but basically it's just an optimization.

If you have multiple trigger patterns, you could even do
Code:

awk '/aaa/ { show[NR+3] = "aaa" }
    /bbb/ { show[NR+3] = "bbb" }
    (NR in show) { printf("%s: %s\n", show[NR], $0) ; delete show[NR] }
    ' infile

although in this simple form it wont tell you if a line was printed for more than one trigger. To list all reasons, you'd need to use for example
Code:

awk '/aaa/ { if ((NR+3) in show) show[NR+3] = show[NR+3] ", aaa" ; else show[NR+3] = "aaa" }
    /bbb/ { if ((NR+3) in show) show[NR+3] = show[NR+3] ", bbb" ; else show[NR+3] = "bbb" }
    (NR in show) { printf("%s: %s\n", show[NR], $0) ; delete show[NR] }
    ' infile


expertshell 10-27-2011 02:34 AM

grail's is a good solution

grail 10-27-2011 07:00 AM

Yes but where mine gets caught is if the pattern appears more than once prior to the line required:
Code:

111111
222222
33aaa3
44aaa4
555555
666666
777777

Here you should get the 6s and the 7s but mine will only print the last entry. A slight alteration you can make to NA's is:
Code:

awk '/aaa/ { show[NR+3]++  } show[NR]' file

cristalp 10-27-2011 01:19 PM

Thanks everyone!
 
Thanks everyone! Your kind and excellent answers expand this small question to a big tank of knowledge and skills. I am glad to see that we share our knowledge so broadly. This is why I join this forum and I hope every one got her progress through the communication.

I, myself, also found another way to solve this problem from a similar problem in another forum:
Code:

grep -A 3 aaa FILENAME | sed -n '4~5p'
Anybody know the meaning of '4~5p'?

Let's keep this question unsolved for a bit while, since I still have some questions on your replies. I will put my questions later on. Please wait for a bit...

David the H. 10-27-2011 01:53 PM

First of all, useless use of grep, since sed can also match text. ;)

Second, did you look in the sed man page? It explains what that pattern means.

But I'd use A1,+N instead, since you want a fixed number of lines after the match, rather than a step.

So use one address range to grab that section of lines, then apply a second nested command to it to print just the line you want.

Code:

sed -n '/aaa/,+3 { 1,+2d ; p }' file.txt
I don't know why, but the nested address matching is kind of tricky. I can't get it to just directly print the last line of the initial match ($ still seems to mean the last line of the file), so you have to tell it to delete everything before it instead.


All times are GMT -5. The time now is 05:24 AM.