This is fairly simple to do with sed really. You just have to balance ranges of lines against one another and as anchored to EOF.
INPUT |
sed -e 's/\\/&&/g;$!s/$/\\/' | #this sed escapes INPUT for scripting
sed -e '/^'"$START"'/,$!{$!b #this sed applies concatenated scripts
G;G;s/$/'"$END"'/;P;:n
};$!N; /\n'"$END"'/,$!{G;$!bn
}; /\n\n/c\' -f - -e 'P;$d;D
' ./named_infile >outfile
So, there are a few things going on there, but the most important of them are these:
/^$START/,$!{ -- function --}
N; /\n$END/,$!{ -- function -- }
The idea is that when we anchor a line range to either of Line 1 or $EOF we have essentially just made it greedy. Usually line ranges apply only to the smallest subset of lines to which they might - starting anew for every LHS match, and ending for the very first RHS match which next occurs in input. If the RHS is EOF though, well, they can only ever be applied once - because there's only one of those.
When I do:
/^$START/,$!{ -- function -- }
I specify that all of the code between the curlies is run for every line in the infile up to but not including $START. In this function context I branch away for every line which is !not the $last.
In this way all lines up to the first $START in input are printed automatically and ignored, but if the $ last line falls within this range - as it might if $START never occurs even once - then it is prepped to be changed to your string.
And so if your range does not occur in input the INPUT gets appended to the tail end of the file.
When I next do:
N; /\n$END/,$!{ -- function -- }
I am again applying a function contextually. This time it is applied to the body of your range - and the only the first occurrence of it input - because the complement of /\n$END/,$ is all lines that were not branched away before the first $START, and only up to and not including the next occurring $END.
In this case the function applied is a branch loop - as long as input falls within that range it will continue to branch back and pull in the Next line until it finds the first $END match, at which point it changes the entire range to the contents of -f - the stdin script file - or your escaped input. This same rule is applied to the last line in the event it occurs before the first $START match.
And that's it. Note though that this does not require any special files to work - because it (safely) incorporates a copy of INPUT within its script, it does not need to read it in at any time to apply when necessary.