This is how to robustly do non-greedy matching of multi-character strings using sed. Lets say you want to change every foo...bar
to <foo...bar>
so for example this input:
$ cat file
ABC foo DEF bar GHI foo KLM bar NOP foo QRS bar TUV
should become this output:
ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV
To do that you convert foo and bar to individual characters and then use the negation of those characters between them:
$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/g; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV
In the above:
s/@/@A/g; s/{/@B/g; s/}/@C/g
is converting {
and }
to placeholder strings that cannot exist in the input so those chars then are available to convert foo
and bar
to.s/foo/{/g; s/bar/}/g
is converting foo
and bar
to {
and }
respectivelys/{[^{}]*}/<&>/g
is performing the op we want - converting foo...bar
to <foo...bar>
s/}/bar/g; s/{/foo/g
is converting {
and }
back to foo
and bar
.s/@C/}/g; s/@B/{/g; s/@A/@/g
is converting the placeholder strings back to their original characters.Note that the above does not rely on any particular string not being present in the input as it manufactures such strings in the first step, nor does it care which occurrence of any particular regexp you want to match since you can use {[^{}]*}
as many times as necessary in the expression to isolate the actual match you want and/or with seds numeric match operator, e.g. to only replace the 2nd occurrence:
$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/2; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC foo DEF bar GHI <foo KLM bar> NOP foo QRS bar TUV