Bash — замена многосимвольной строки, когда строки состоят из неизвестной длины, но одного и того же символа

Предположим, что это многострочная текстовая строка, в которой некоторые строки начинаются с ключевого символа ("#" в нашем случае). Далее предположим, что вы хотите заменить все экземпляры целевого символа ("o" в нашем случае) другим символом ("O" в нашем случае), если - и только если - этот целевой символ встречается в виде строки из двух или более смежных копий (например, «ооо»). Эта замена должна выполняться во всех строках, которые не начинаются с ключевого символа, и должна учитывать регистр.

Например, следующие строки...

#Foo bar
Foo bar
#Baz foo
Baz foo

должны быть преобразованы в:

#Foo bar
FOO bar
#Baz foo
Baz fOO

Следующая попытка использования sed не сохраняет правильное количество целевых символов:

$ echo -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | sed '/^#/!s/o\{2,\}/O/g'
#Foo bar
FO bar
#Baz foo
Baz fO

Какой код (со знаком sed или каким-либо иным образом) правильно произведет желаемую замену?

Вы должны были включить пример только с 1 o, который вы не хотели заменять, чтобы продемонстрировать 2 или более o часть вашего вопроса. Вы также должны были включить строку с несколькими наборами o для замены. Строка типа Foo box good будет проверять случаи, которых нет в существующем примере.

Ed Morton 03.04.2022 05:46
В чем разница между методом "==" и equals()
В чем разница между методом "==" и equals()
Это один из наиболее часто задаваемых вопросов новичкам на собеседовании. Давайте обсудим его на примере.
Замена символа по определенному индексу в JavaScript
Замена символа по определенному индексу в JavaScript
В JavaScript существует несколько способов заменить символ в строке по определенному индексу.
2
1
54
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Использование sed

$ echo -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | sed '/#/!s/o\{2\}/\U&/'
#Foo bar
FOO bar
#Baz foo
Baz fOO

Но позволяет ли эта команда sed заменить целевой символ другим символом (например, «ооо» заменить на «ннн»)? Я не думаю, что это так.

Michael Grünstäudl 02.04.2022 23:19

@MichaelGrünstäudl: выбранный вами пример неуместен

Cyrus 02.04.2022 23:23

@MichaelGrünstäudl Конечно, просто измените замену с того, что уже было, на то, что вы хотели бы, чтобы t было, например, echo -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | sed '/#/!s/o\+/\Unnn/' или, если требуется нижний регистр, удалите \Uecho -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | sed '/#/!s/o\+/nnn/'

HatLess 02.04.2022 23:23

Без шляпы @MichaelGrünstäudl точка зрения заключается в том, что вы не можете (легко) написать сценарий sed, который заменит N «o» на N вхождений какого-либо другого символа. В своем ответе вы жестко закодировали 3 n, которые, очевидно, не сработали бы, если бы ввод содержал 2 или 4 o. ОП также хотел выполнить замену только в том случае, если присутствовало 2 или более смежных o, поэтому o\+ необходимо было настроить.

Ed Morton 03.04.2022 05:39

@EdMorton Спасибо за разъяснение. из вопроса не понятно

HatLess 03.04.2022 13:51
Ответ принят как подходящий

Вы можете использовать Perl:

echo -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | perl -pe 's/^#.*(*SKIP)(*F)|o{2,}/"O" x length($&)/ge'

Здесь ^#.*(*SKIP)(*F) соответствует и пропускает все строки, начинающиеся с #, затем o{2,} соответствует двум или более символам o, а "O" x length($&) заменяет эти совпадения на O, которое повторяется количество раз совпадения ($& — значение совпадения). Обратите внимание на флаг e после g, который используется для оценки строки в правой части.

См. онлайн демо:

#!/bin/bash
s = "#Foo bar
Foo bar
#Baz foo
Baz foo"
perl -pe 's/^#.*(*SKIP)(*F)|o{2,}/"O" x length($&)/ge' <<< "$s"

Выход:

#Foo bar
FOO bar
#Baz foo
Baz fOO

Это может сработать для вас (GNU sed):

sed -E '1{x;s/^/O/;x}
        /^#/b
       :a;/oo+/!b;s/oo+/\n&\n/;tb
       :b;G;s/\n\n(.*)\n.$/\1/;ta;s/\n[^\n](.*\n.*)\n(.)$/\2\n\1/;tb' file

В общем, используйте обозначенный символ или символы (в данном случае O) для замены двух или более o там, где начало строки не #.

Заполните пространство для удержания указанным персонажем.

Если строка начинается с #, оборвать.

Если в строке нет двух или o, вырваться.

В противном случае окружите два или более o символами новой строки.

Добавьте заменяющий символ, а затем замените символы, не являющиеся новой строкой, между двумя символами новой строки указанным символом.

Когда все замены для текущего набора o будут заменены, проверьте наличие дополнительных, продолжая, как указано выше.

Как только все замены будут найдены, выведите исправленную строку.


Решение, допускающее множественные замены:

sed -E '1{x;s/^/oOxX/;x}
        /^#/b;
        :a;G;/((.)\2+)(.*\n(..)*\2)/!s/\n.*//;t;s//\n\1\n\3/;tb
        :b;s/\n\n(.*)\n.*$/\1/;ta;s/\n(.)(.*\n.*\n(..)*\1(.))/\4\n\2/;tb' file

Другие вопросы по теме