How to recursively replace characters with sed?

You can do:

> echo XXXX | sed -e ':loop' -e 's/XX/XoX/g' -e 't loop'
XoXoXoX

With:

  • -e ':loop' : Create a "loop" label
  • -e 't loop' : Jump to the "loop" label if previous substitution was successful

In this particular case look-ahead or look-behind would be useful. I think GNU sed doesn't support these. With perl:

perl -ne 's/X(?=X)/Xo/g; print;'

You could also use lookbehind and lookahead like:

s/(?<=X)(?=X)/o/g

Where:

(?<=X) is a positive lookbehind, a zero-length assertion that make sure we have an X before the current position
(?=X) is a positive lookahead, a zero-length assertion that make sure we have an X after the current position

Using in a perl one-liner:

perl -pe 's/(?<=X)(?=X)/o/g' inputfile

Where:

-p causes Perl to assume a loop around the program with an implicit print of the current line


The looping answer is the general way to do what you are asking.

However in the case of your data, assuming you are using GNU you can simply do:

sed 's/\B/o/g'

The \b and \B options are regex extensions:

  • \b matches word boundaries, i.e. the transition from a "word" character to "non-word" character, or vice-versa
  • \B matches the opposite of \b. i.e. the gaps "inside" words. This allows us to insert characters inside of a word but not outside, as required.

Try it online.

This assumes that the input characters are in fact all "word" characters.


Alternatively if you don't have GNU sed, or if the input characters are not all "word" characters, you can still achieve your goal without looping:

sed 's/./&o/g;s/o$//'

This simply places an o after every character and then removes the final o from the string.

Try it online.