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
match5 further lines are appended.
The details are as follows:
:ais a loop placeholder$qon end-of-file print all lines within the pattern space (PS).Nappend the next line to the PSs/n/&/3replace the 3rd newline character by itself. This a counting device for checking that 3 lines are in the PS.Taif the previous substitution failed loop to the loop placeholdera/match/!{P;D}look thematchand if it fails print upto the first newline and then delete that line and it’s newline (this invokes a new cycle).:bis a loop placeholder N.B. a match has been found at this point.$bcif end-of-file branch forward to the placeholdercNappend the next line to the PSs/n/&/8replace the 8th (3 before 5 after) newline character by itself. This a counting device for checking that 5 lines are appended to the PSTbif the previous substitution failed loop to the loop placeholderb:cis 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