All things considered, gobbling the entire file may be the fastest way to go.
Basic syntax is as follows:
sed -e '1h;2,$H;$!d;g' -e 's/__YOUR_REGEX_GOES_HERE__...'
Mind you, gobbling the entire file may not be an option if the file is tremendously large. For such cases, other answers provided here offer customized solutions that are guaranteed to work on a small memory footprint.
For all other hack and slash situations, merely prepending -e '1h;2,$H;$!d;g'
followed by your original sed
regex argument pretty much gets the job done.
e.g.
$ echo -e "Dog\nFox\nCat\nSnake\n" | sed -e '1h;2,$H;$!d;g' -re 's/([^\n]*)\n([^\n]*)\n/Quick \2\nLazy \1\n/g'
Quick Fox
Lazy Dog
Quick Snake
Lazy Cat
What does -e '1h;2,$H;$!d;g'
do?
The 1
, 2,$
, $!
parts are line specifiers that limit which lines the directly following command runs on.
1
: First line only
2,$
: All lines starting from the second
$!
: Every line other than last
So expanded, this is what happens on each line of an N line input.
1: h, d
2: H, d
3: H, d
.
.
N-2: H, d
N-1: H, d
N: H, g
The g
command is not given a line specifier, but the preceding d
command has a special clause "Start next cycle.", and this prevents g
from running on all lines except the last.
As for the meaning of each command:
- The first
h
followed by H
s on each line copies said lines of input into sed
's hold space. (Think arbitrary text buffer.)
- Afterwards,
d
discards each line to prevents these lines from being written to the output. The hold space however is preserved.
- Finally, on the very last line,
g
restores the accumulation of every line from the hold space so that sed
is able to run its regex on the whole input (rather than in a line-at-a-time fashion), and hence is able to match on \n
s.
.
,*
,^
,$
and[
...]
.