10

I'd like to search for text that may be split over several lines in a file. A grep that would ignore line breaks and return the matching span of lines.

e.g. I would be searching for is an example file, and expect it to be found in the following file:

This is
an
example file.

Not to depend on leading or trailing spaces, entirely ignoring all forms of white space might be best (ideally, treating any sequence of white space as a single space).


One non-ideal solution is tr '\n' ' ' | grep, that discriminates between matches and non-matches, but doesn't show the match, nor deals well with big files.

  • on SO (no definitive answer) : stackoverflow.com/q/1858312/1449460 – Nikana Reklawyks May 18 '15 at 17:26
  • As a side note, emacs' search seems to do the job (isearch-forward) – Nikana Reklawyks May 18 '15 at 17:33
  • So does Vim's: /This\_sis. For more details: :help \_s. – lcd047 May 18 '15 at 17:37
  • Add this line at the end of your search line: tr -n "\n" This will remove all new lines. Hope this help! – Dan Howel Aug 18 '17 at 18:28
11

The GNU grep can do it

grep -z 'is\san\sexample\sfile.' file

To fulfill some points which arise in comments there are some modifications to script:

 grep -oz '^[^\n]*\bis\s*an\s*example\s*file\.[^\n]*' file

Regarding huge files I have no imagination of memory limitation but in the case of problem you are free to use sed

sed '/\bis\b/{
          :1
          N
          /file\.\|\(\n.*\)\{3\}/!b1
         }
     /\<is\s*an\s*example\s*file\./p
     D' file

that keep no more than 4-lines (because 4 words in pattern) in memory (\(\n.*\)\{3\}).

  • 4
    As I’m sure you know, the -z option tells grep to treat newlines as ordinary text characters, and look for nul bytes to separate records.  In a text file with no nul bytes (i.e., the typical case), grep -z will treat the entire file as one line.  So (1) this raises the question of how well it can handle large files, and (2) if it finds a match, it will write out the entire file, giving no clue as to the location of the match.  Also (3) the OP said, “ideally, treating any sequence of white space as a single space,” so you should use \s+ and add -E. – G-Man May 18 '15 at 23:38
  • 1
    @G-Man Thank you for comment. Please see edited answer. – Costas May 19 '15 at 9:28
  • 1
    (0) Ah, -o; I keep forgetting about that.  Clever way to use it.  (1) Your new grep answer begins ^[\n]*; that’s a typo for [^\n]*.  (2) I said \s+ deliberately.  be\s*little will match belittle, and care\s*less will match careless.  But I guess that’s a minor problem.  And, if you don’t want to use -E, you can use “the poor man’s version” of \s+, namely, \s\s*.  (3) Nice sed command.  It can fail if there are blank lines (so the four-word phrase can spread over more than four lines); I was able to fix that by adding s/\n\s*\n/\n/. – G-Man May 20 '15 at 1:10
  • @G-Man Thank you againg. Your comments are very useful. I have try to post more or less portable code because famous members everytime push me to do so. Anyway even without -E you steel able to use + in \s\+ form. Empty lines inside of pattern seems to be a contrived. – Costas May 20 '15 at 8:55
  • I was thinking of paginated text documents, like RFCs — ISTR that man pages look like that on some systems (or did) — but, on further thought, it occurs to me that most such documents have page header(s) and/or footer(s) that would need to be stripped out before you could hope to grep them for phrases. – G-Man May 20 '15 at 16:44
7

Try this:

pcregrep -M '\bThis\s+is\b' <<EOT
This
is
an example
file.
EOT
  • Do I have to type \s 5 times if I search for "this is a very long pattern" ? – Nikana Reklawyks May 18 '15 at 17:31
  • 1
    Yes: the point is \s matches spaces, and newline is a "space". – lcd047 May 18 '15 at 17:32
  • I mean, what if the file is This\nis a very\nlong pattern, and I don't know where the line breaks might occur. I'd have to search for This\sis\sa\svery\slong\spattern, right ? (which becomes tedious as the length of the pattern increases or is pasted from elsewhere) – Nikana Reklawyks May 18 '15 at 17:36
  • 2
    Then you do it like this: pcregrep -M "$( echo 'This is a very long pattern' | sed 's/ /\\s+/g' )" file. – lcd047 May 18 '15 at 17:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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