Как рекурсивно заменить символы на sed?

13

Можно ли рекурсивно заменить вхождения последовательности символов без повторения итерации по той же самой последовательности?

Выполняя sedкак в следующих сценариях, я могу получить упомянутый вывод.

$ echo XX | sed -e 's/XX/XoX/g'
XoX  
$ echo XXX | sed -e 's/XX/XoX/g'
XoXX  
$ echo XXXX | sed -e 's/XX/XoX/g'
XoXXoX  

Тем не менее, я ожидаю, что результат будет соответствовать следующему.

Входные данные:

XX
XXX
XXXX

Ожидаемый результат:

XoX
XoXoX
XoXoXoX

Можно ли добиться ожидаемого поведения с помощью одного только sed?

Ишан Мадхусанка
источник

Ответы:

24

Ты можешь сделать:

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

С:

  • -e ':loop' : Создать ярлык "петли"
  • -e 't loop' : Переход к метке «loop», если предыдущая замена прошла успешно
Gohu
источник
10

В этом конкретном случае было бы полезно забегать вперед или оглядываться назад. Я думаю, что GNU sedне поддерживает это. С perl:

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

Вы также можете использовать lookbehind и lookahead, например:

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

Где:

(?<=X)является положительным взглядом сзади, утверждение нулевой длины, которое гарантирует, что у нас есть X перед текущей позицией,
(?=X)является положительным взглядом вперед, утверждение нулевой длины, которое гарантирует, что у нас есть X после текущей позиции

Использование в perl однострочном:

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

Где:

-p заставляет Perl предполагать цикл вокруг программы с неявным выводом текущей строки

Toto
источник
5

Циклический ответ - это общий способ сделать то, что вы просите.

Однако в случае ваших данных, если вы используете GNU, вы можете просто сделать:

sed 's/\B/o/g'

\bИ \Bварианты регулярных выражений расширений :

  • \b соответствует границам слова, то есть переходу от символа «слово» к символу «не слово», или наоборот
  • \Bсоответствует противоположности \b. то есть пробелы "внутри" слова. Это позволяет нам вставлять символы внутри слова, но не снаружи, как требуется.

Попробуйте онлайн .

Это предполагает, что входные символы на самом деле являются символами «слова».


В качестве альтернативы, если у вас нет GNU sed или если входные символы не все «слово», вы все равно можете достичь своей цели без зацикливания:

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

Это просто помещает oпосле каждого символа, а затем удаляет финал oиз строки.

Попробуйте онлайн .

Цифровая травма
источник
1
Это предполагает, что входные строки состоят из некоторого числа Xи ничего больше. Оба решения терпят неудачу, если присутствуют другие персонажи ...
AnoE
@AnoE Во втором примере это исправлено простой заменой Xна .. Пожалуйста, смотрите редактировать.
Цифровая травма
Не соответствует случаю, который выдал ОП. Он дал именно те RE, которые ему нужны (измените вхождения XX в строке). Ваши версии дают тот же результат, что и его, для тех же самых входных строк, которые он дал; не для общих строк ввода.
AnoE
4

Я проверил, есть ли какой-нибудь флаг, чтобы это произошло.
Даже если бы такое поведение было там, оно будет очень ресурсоемким.

Однако в этом конкретном случае использования можно получить выражение всего два раза и достичь требуемой функциональности. т.е. с 2 повторяющимися sedвыражениями.

echo XX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'     # outputs XoX
echo XXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'    # outputs XoXoX
echo XXXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'   # outputs XoXoXoX
Ишан Мадхусанка
источник