4

I'm having trouble on replacing the Nth line with "replaceString" within range-match in the sed command, i.e, is it possible to replace relatively the Nth line within range-match?

consider this input data:

1 a start 3 h 4 7 end 6 iii 7 4 

and having this command:

sed '/start/,/end/ { *s/.*/replaceString/ }' 

in place of * in the above command how do I relatively say do command s/.*/replaceString/ say on 2nd line of the matched range? so it will give me following expected output.

1 a start replaceString 4 7 end 6 iii 7 4 

this changes the second line absolute to the whole input file which is not what I want.

$ sed '/start/,/end/{ 2s/.*/replaceString/}' infile 1 a replaceString 3 h 4 7 end 6 iii 7 4 

kindly note that I'm specifically want to do this in sed command to knowing how to relatively say line number for the matched range. the position (line number) of the start and end patterns are unknown.

2 Answers 2

2

With GNU sed you could use the addr1,+N address range as a machinery to count inner lines, like this:

sed '/start/,/end/{/start/,+1{/start/,+0!s/.*/replaceString/}}' # -------------------------------------^ This is Nth - 2 # --------------------------^ This is Nth - 1 # So for instance to replace the 4th line after "start" # (the 5th including "start") you would do: sed '/start/,/end/{/start/,+4{/start/,+3!s/.*/replaceString/}}' 

So basically the rule of thumb would be to replace your * "placeholder address" with the following expression:

/<first-pattern>/,+<N-1>{/<first-pattern>/,+<N-2>! # of course ended by one right brace after the series of # commands to execute for the Nth inner line 

POSIXly another approach would be by using the hold space as accumulator to keep count of lines, like the following sed script:

#!/usr/bin/sed -f # In practice we add a '-' to hold-space for each inner line # until we have added as many '-' as the number of the # wanted Nth inner line. # Here is an example to replace the 4th line after # "start" (the 5th including "start") /start/,/end/{ x;/^-\{0,4\}$/{ # here the numbers within braces must be 0,N-1 s/^/-/;/-\{5\}/{ # here the number within braces must be N x;s/.*/replaceString/;bdone } } x :done <here do text-manipulation common to all lines of start/end blocks> b # _skip_ clearing the accumulator kept in hold-space } # clear the accumulator that we kept in hold-space x;s/.*//;x 

An inconvenience is that we keep the hold space occupied during the counting, but at least we consume lines without buffering them so it should work on files of any size.

Note that both are only a quick thought, so there might be room for improvement.

1
  • I think the gsed option (1) the innermost nested part ranges from start up to the last line of the previously defined, the parent's, range to not act upon, thus only act on the last line of the parent's defined range. Commented Jan 9, 2022 at 23:39
1

Perhaps this way :

sed '/start/{:a;N;/end/!ba;s/[^\n]*/replaceString/2}' file 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.