Предположим, что это многострочная текстовая строка, в которой некоторые строки начинаются с ключевого символа ("#" в нашем случае). Далее предположим, что вы хотите заменить все экземпляры целевого символа ("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 или каким-либо иным образом) правильно произведет желаемую замену?


Использование 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 заменить целевой символ другим символом (например, «ооо» заменить на «ннн»)? Я не думаю, что это так.
@MichaelGrünstäudl: выбранный вами пример неуместен
@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/'
Без шляпы @MichaelGrünstäudl точка зрения заключается в том, что вы не можете (легко) написать сценарий sed, который заменит N «o» на N вхождений какого-либо другого символа. В своем ответе вы жестко закодировали 3 n, которые, очевидно, не сработали бы, если бы ввод содержал 2 или 4 o. ОП также хотел выполнить замену только в том случае, если присутствовало 2 или более смежных o, поэтому o\+ необходимо было настроить.
@EdMorton Спасибо за разъяснение. из вопроса не понятно
Вы можете использовать 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
Вы должны были включить пример только с 1
o, который вы не хотели заменять, чтобы продемонстрировать 2 или болееoчасть вашего вопроса. Вы также должны были включить строку с несколькими наборамиoдля замены. Строка типаFoo box goodбудет проверять случаи, которых нет в существующем примере.