Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

How can I grep a directory for lines that contain "Foo", but only get matches when the next line also contains "Bar"?

share|improve this question
    
The problem is now totally different than the original :/ Maybe better to revert the old versions & POST another one ? Moreover the new question is not clear for me. –  sputnick Oct 8 '12 at 17:03
    
@sputnick - how so? I specified a directory when I first posted the question; I only bolded it because people weren't noticing. –  Nathan Long Oct 8 '12 at 17:06
    
Nevermind, that will works, I will edit my POST accordingly. –  sputnick Oct 8 '12 at 17:10

6 Answers 6

Grep itself doesn't seem to support it, use pcregrep instead:

Foo
Bar
Foo
abc

pcregrep -M "Foo\nBar" file

Got:

Foo
Bar
share|improve this answer
3  
The OP didn't say that Foo and Bar would comprise the entire line. –  tojrobinson Oct 8 '12 at 15:46

Using grep only, you could construct the following pipe:

grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'

The first grep will get all the lines that contain Foo as well as the line after the match. Then we get lines that contain Bar as well as the line before the match, and finally extract the lines from this output that contain Foo.

EDIT: As manatwork pointed out, there are some problematic cases to be observant of. Although an interesting challenge, due to grep's line oriented functionality, any solution with it is likely to be a 'hack' and you are probably better off using something like pcregrep which is more suited to the task at hand.

share|improve this answer
    
Nice. I asked about a directory though; this seems to work: find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar' –  Nathan Long Oct 8 '12 at 16:07
    
That will also list occurrences with both “Foo” and “Bar” on the same line. –  manatwork Oct 8 '12 at 16:12
    
@manatwork: Lines that contain "Foo" and "Bar" are "lines that contain 'Foo'" which is was what was asked. –  tojrobinson Oct 8 '12 at 16:15
1  
@tojrobinson, what about the “but only get matches when the next line also contains "Bar"” part? pastebin.com/Yj8aeCEA –  manatwork Oct 8 '12 at 16:28

@warl0ck pointed me in the right direction with pcregrep, but I said "contains", not "is", and I asked about a directory, not a file.

This seems to work for me.

pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
share|improve this answer
    
So accept warl0cks answer. –  Nils Oct 11 '12 at 20:35

With a sed script :

#!/bin/sed -nf

/^Foo/{
    h         # put the matching line in the hold buffer
    n         # going to nextline
    /^Bar/{   # matching pattern in newline
        H     # add the line to the hold buffer
        x     # return the entire paragraph into the pattern space
        p     # print the pattern space
        q     # quit the script now
    }
}

To use it :

chmod +x script.sed
printf '%s\n' * | ./script.sed

The printf here display all files in the current directory on one line each, and pass it to sed.

Note : this is sorted by alphabetical order.

More infos of useful pattern space and hold space HERE.

grymoire.com have really good stuff about shell programming.

share|improve this answer
    
What does h, n, H, x, p, q means? Very interesting. –  Yamaneko Oct 8 '12 at 16:40
    
See my comments. More infos on pattern space & hold space : grymoire.com/Unix/Sed.html#uh-56 or in french commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i –  sputnick Oct 8 '12 at 16:43
    
POST adapted to work on a directory –  sputnick Oct 8 '12 at 17:12

With awk:

awk '/bar/ && prev != "" {print FILENAME ": " prev "\n" FILENAME ": " $0}
     /foo/ {prev=$0; next}
     {prev=""}' file1...

(general note about awk limitation: beware that if some file names may contain "=" characters, you'll need to pass them as ./filename instead of filename to awk)

share|improve this answer

While I prefer Nathan's solution using pcregrep, here is solution using only grep

grep -o -z -P  'Foo(.*)\n(.*)Bar' file

Options explanation:

  • -o print only matched part. Necessary since inclusion of -z will printout the whole file (unless there is a \0 somewhere)
  • -z Treat the input as a set of lines, each terminated by a zero byte (the ASCII NUL character) instead of a newline.
  • -P perl regex syntax

EDIT : This version prints out entire matched lines

    grep -o -P -z  '(.*)Foo(.*)\n(.*)Bar(.*)' file
share|improve this answer
1  
Cool trick what -z. Some “(.*)” before and after the whole expression would make it output the entire matched lines. For now substrings before “Foo” and after “Bar” are not displayed. –  manatwork Oct 9 '12 at 8:19
    
Good point, thanks –  bbaja42 Oct 9 '12 at 8:54

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.