In sed
, I’d like to replace a multi-line block of text after a match, for example, after matching “foo”, supposing its line number is 0. I want to replace the text block from line -3 to line +5, i.e. the text bock between the third line proceeding the matching line and the fifth line after the matching line, by another text block bar1nbar2
. I’d like to be able to do this in two scenarios:
1) Keep the matching line after the replaced block; 2) remove the matching line together with those lines -3 and +5.
Please help me.
Thank you.
Advertisement
Answer
This might work (GNU sed):
seq 31|sed 's/5/& match/' >/tmp/file sed ':a;$q;N;s/n/&/3;Ta;/match/!{P;D};:b;$bc;N;s/n/&/8;Tb;:c;s/.*/bar1nbar2/' /tmp/file 1 bar1 bar2 11 bar1 bar2 21 bar1 bar2 31 sed ':a;$q;N;s/n/&/3;Ta;/match/!{P;D};h;s/([^n]*n)*([^n]*match[^n]*).*/2/;x;:b;$bc;N;s/n/&/8;Tb;:c;s/.*/bar1nbar2/;G' /tmp/file 1 bar1 bar2 5 match 11 bar1 bar2 15 match 21 bar1 bar2 25 match 31
Explanation:
The commands fall into two halves:
- The first half keeps a moving window of 3 lines.
- Following a
match
5 further lines are appended.
The details are as follows:
:a
is a loop placeholder$q
on end-of-file print all lines within the pattern space (PS).N
append the next line to the PSs/n/&/3
replace the 3rd newline character by itself. This a counting device for checking that 3 lines are in the PS.Ta
if the previous substitution failed loop to the loop placeholdera
/match/!{P;D}
look thematch
and if it fails print upto the first newline and then delete that line and it’s newline (this invokes a new cycle).:b
is a loop placeholder N.B. a match has been found at this point.$bc
if end-of-file branch forward to the placeholderc
N
append the next line to the PSs/n/&/8
replace the 8th (3 before 5 after) newline character by itself. This a counting device for checking that 5 lines are appended to the PSTb
if the previous substitution failed loop to the loop placeholderb
:c
is a loop placeholders/.*/bar1nbar2/
replace the PS with the required string.
The second one liner makes a copy of the match
line and appends it to the substituted string.
Alternative solutions:
sed -r ':a;$!N;s/[^n]*/&/9;$!Ta;/^([^n]*n){3}([^n]*match[^n]*)n.*/!{P;D};cbar1nbar2' file sed -r ':a;$!N;s/[^n]+/&/9;$!Ta;/^([^n]*n){3}([^n]*match[^n]*)n.*/!{P;D};s//bar1nbar2n2/' file