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):
JavaScript
x
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:
JavaScript
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