Skip to content
Advertisement

how to replace a block of text after a match in sed

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:

  1. The first half keeps a moving window of 3 lines.
  2. 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 PS
  • s/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 placeholder a
  • /match/!{P;D} look the match 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 placeholder c
  • N append the next line to the PS
  • s/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 PS
  • Tb if the previous substitution failed loop to the loop placeholder b
  • :c is a loop placeholder
  • s/.*/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
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement