2

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.

1

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

Perhaps this way :

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.