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

1686
Ishan Madhusanka

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

Выполняя 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?

13

4 ответа на вопрос

23
Gohu

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

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

С:

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

В этом конкретном случае было бы полезно забегать вперед или оглядываться назад. Я думаю, что 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 предполагать цикл вокруг программы с неявным выводом текущей строки

5
Digital Trauma

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

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

sed 's/\B/o/g' 

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

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

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

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


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

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

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

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

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

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

Однако в этом конкретном случае использования можно получить выражение всего два раза и достичь требуемой функциональности. т.е. с 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 

Похожие вопросы