14

How do I search for a phrase over multiple lines? E.g. Lets have the phrase "my ice tea" then it may be wrapped in text files:

as js skdfh dfh djh sf my
ice tea.

grep will not match since there is a newline in between. How do I match those? Another multiline pattern would be pattern1_\n_pattern2

I know the easiest way I do ATM is just grep for one part e.g. just ice with -A2 -B2 flag and then in that output againg for e.g. tea. But this is very tedious. So I am interessted on how do you would solve this.

1

5 Answers 5

17

You could install pcregrep (available in most distro repositories) - which is grep using the pcre library, which does "Perl Compatible Regular Expressions". It has a command line option -M which allows you to do multiline searches - from the man page:

"The output for any one match may consist of more than one line."

So you could do

pcregrep -M 'my\s+ice\s+tea' filename

The \s is whitespace, which will match \n and \r in multiline mode, in addition to the normal whitespace characters. You can also match the newline character directly, so you could do

pcregrep -M 'pattern1_\n_pattern2' filename
4
  • +1 nice. never heard of it, but tried it and it works like a charm! Jul 20, 2010 at 15:38
  • Doesn't grep -E do pcre patterns?
    – Daenyth
    Jul 20, 2010 at 17:40
  • 4
    @Daenyth grep -E mostly just means you can use ?, +, {, |, (, and ) as their usual regex meaning without having to have a \ in front, as you do if you use standard grep. So grep 'hello\s\+world' file is equivalent to grep -E 'hello\s+world' file. It doesn't do PCRE. There is grep -P for perl regular expressions, but it is experimental (according to the man page) and I think it is a little different from pcregrep ... Jul 20, 2010 at 21:17
  • 1
    Yes, I was thinking of -P when I said -E, but I didn't realize it was different.
    – Daenyth
    Jul 21, 2010 at 2:31
3

I would probably do a search using vim's :vimgrep command. This works in a manner vaguely similar to that of grep but supports vim REs and paths.

Basically you run something like :vimgrep 'pattern1\npattern2' path/** for a recursive search, then type :copen to bring up a smaller window containing a list of matches.

vim REs can do mostly everything that PCREs can, but they evolved separately from the perl regular expression lineage so most of the advanced stuff works differently. Their basic functionality is more like that of basic REs, but they have some nifty additions that PCREs don't offer.

I'm not sure if it's possible to get :vimgrep to spit out data as grep does; I've only ever tried to use it for navigation within vim.

:help vimgrep from within vim for more info; :help pattern.txt for info on vim REs; for more info on paths see :help wildcards.

5
  • Be careful -- that's not entirely portable as it will behave differently on different platforms
    – Daenyth
    Jul 20, 2010 at 17:41
  • 1
    @Daenyth: do you mean under the influence of different .vimrc's? It should be more portable than grep with respect to the operating system: vim doesn't have a "POSIX flavour", and works more or less identically even under Windows. ............................................................................. It's possible to add qualifiers to ensure that e.g. the right amount of "magic" gets used on the RE, though as I understand it there is a strict unwritten rule to leave that option the heck alone.
    – intuited
    Jul 21, 2010 at 4:55
  • I haven't used it myself, but apparantly it uses a different backend on windows (find.exe instead of grep). There was another question within the last few weeks that had that issue.
    – Daenyth
    Jul 21, 2010 at 13:01
  • 1
    @Daenyth: Are you thinking of :vimgrep or :grep? From :help grep: "The advantage of the internal grep [i.e. :vimgrep] is that it works on all systems and uses the powerful Vim search patterns."
    – intuited
    Jul 21, 2010 at 13:35
  • 1
    Ah, that must be it. I got the two confused.
    – Daenyth
    Jul 21, 2010 at 15:36
3

Grep only works on one line at a time, but you could use awk to print lines matching a range of patterns:

cat file | awk '/foo/,/bar/'

it would match anything, not just newlines between the two patterns

0

To get the most out of unix you need to take advantage of pipes. You can do this in plain grep using pipes (no need for tee):

$ grep -A1 "pattern1" file.txt |  grep "pattern2"

Which I wouldn't consider tedious.

1
  • I think this is error prone, as between pattern1 and pattern2 can exists pattern3 which might not be what your looking for. So you have to control each hit manually.
    – math
    Feb 17, 2015 at 8:16
0

I believe the following should work and has the advantage of only using extended regular expressions without the need to install an extra tool like pcregrep if you don’t have it yet or don’t have the -P option to grep available (eg. macOS):

egrep -irzo “.*aaa(.*\s.*){1,}.*bbb.*" path_to_filenames

Caveat emptor: this does some slight disadvantages:

  • it will find the largest selection of lines from the first aaa to the last bbb in each file, unless...
  • there are several repetitions of the aaa [stuff] bbb pattern in each file.

You must log in to answer this question.

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