With sed
you can do this without slurping the entire file:
sed -e ':top' -e 's/john paul[[:space:]]*george/pete/g;$b' -e '/john paul[[:space:]]*$/!b' -e 'N;btop' input
This is much lighter on memory usage; it only slurps multiple lines when there is a possibility of a multi-line match starting from the current line. And then it only slurps until either the match is found, or until there is no further possibility of a match.
As a bonus, it's POSIX-compliant. (Perl isn't part of POSIX.) Thanks to mikeserv for pointing this out in the comments.
Explanation:
:top
sets a label named top
.
s/john paul[[:space:]]*george/pete/g
does the substitution you want for whatever is in the pattern space. (Default is line by line.)
$b
skips to the end and prints if the current line is the last line of the file.
/john paul[[:space:]]*$/!b
:
The pattern /john paul[[:space:]]*$/
will match john paul
at the end of the pattern space followed by any amount of whitespace (but nothing other than whitespace), then !
inverts the pattern. So the effect here is to execute the b
command (skip to the end of the script, thus printing the pattern space, reading the next line from the file, and starting from the top of the script) only if there is no possibility of a multi-line match starting with the current pattern space.
N
appends the next line from the file to the pattern space (after appending a newline).
btop
branches to the :top
label without clearing out the pattern space.
perl
, but with bsd or gnused
:sed -E 's/((george|john|paul) *){2}/pete/g'
should work... – mikeserv Jan 15 '16 at 20:16john paul
is a single string, not two strings. And thatsed
command wouldn't handle matches broken across multiple lines. – Wildcard Jan 15 '16 at 22:31