Обзор из многих полезных существующих ответов , дополненных с пояснениями :
В приведенных здесь примерах используется упрощенный вариант использования: замените слово «foo» на «bar» только в первой строке соответствия.
Благодаря использованию ANSI C-строк в кавычках ( $'...'
) , чтобы обеспечить выборки входных линий, bash
, ksh
или zsh
предполагаются в качестве оболочки.
sed
Только GNU :
Anwswer Бен Hoffstein в показывает нам , что GNU обеспечивает расширение к спецификации POSIX дляsed
, что позволяет следующей формы 2-адрес : 0,/re/
( re
представляет собой произвольное регулярное выражение здесь).
0,/re/
позволяет регулярному выражению совпадать и в самой первой строке . Другими словами: такой адрес создаст диапазон от 1-й строки до и включая строку, которая соответствует re
- re
происходит ли в 1-й строке или в любой последующей строке.
- Сравните это с POSIX-совместимой формой
1,/re/
, которая создает диапазон, который соответствует от 1-й строки до и включает строку, которая соответствует re
в последующих строках; другими словами: это не обнаружит первое re
совпадение, если оно произошло в 1-й строке, а также предотвратит использование сокращения//
для повторного использования последнего использованного регулярного выражения (см. следующий пункт). 1
Если вы объединяете 0,/re/
адрес с s/.../.../
вызовом (подстановки), который использует то же регулярное выражение, ваша команда будет эффективно выполнять подстановку только в первой соответствующей строке re
.
sed
обеспечивает удобный ярлык для многократного использования самого последнего применяется регулярное выражение : пустой пары разделителей,//
.
$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Только для POSIX-функций, sed
таких как BSD (macOS)sed
(также будет работать с GNU sed
):
Поскольку 0,/re/
невозможно использовать и форма 1,/re/
не обнаружит, re
происходит ли это в самой первой строке (см. Выше), требуется специальная обработка для 1-й строки .
В ответе MikhailVS упоминается методика, приведенная здесь на конкретном примере:
$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Примечание:
Пустой //
ярлык регулярного выражения используется здесь дважды: один раз для конечной точки диапазона и один раз в s
вызове; в обоих случаях регулярное выражение foo
неявно используется повторно, что позволяет нам не дублировать его, что делает как более короткий, так и более понятный код.
POSIX sed
нужны реальные переводы строк после определенных функций, таких как после имени метки или даже ее пропуска, как в случае с t
здесь; Стратегическое разделение сценария на несколько -e
вариантов является альтернативой использованию фактических строк новой строки: заканчивайте каждый -e
фрагмент сценария там, где обычно должен идти символ новой строки.
1 s/foo/bar/
заменяет только foo
на 1-й строке, если найден там. Если это так, происходит t
переход к концу скрипта (пропускаются оставшиеся команды в строке). ( t
Функция переходит к метке только в том случае, если самый последний s
вызов выполнил фактическую замену; при отсутствии метки, как в данном случае, конец сценария разветвляется).
Когда это происходит, адрес диапазона 1,//
, который обычно находит первое вхождение, начиная со строки 2 , не будет совпадать, и диапазон не будет обрабатываться, поскольку адрес оценивается, когда текущая строка уже существует 2
.
И наоборот, если на 1-й строке нет совпадений, 1,//
будет введено и найдет истинное первое совпадение.
Чистый эффект такой же , как и с GNU sed
«s 0,/re/
: только первое вхождение заменяется, происходит ли это на 1 - й линии , или любой другой.
Бездиапазонные подходы
Ответ Потонга демонстрирует петлевые техники, которые обходят необходимость в диапазоне ; так как он использует синтаксис GNU sed
, вот POSIX-совместимые эквиваленты :
Техника цикла 1: при первом совпадении выполните подстановку, затем введите цикл, который просто печатает оставшиеся строки как есть :
$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
Техника цикла 2, только для небольших файлов : прочитать весь ввод в память, а затем выполнить одну подстановку .
$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
1 1.61803 предоставляет примеры того, что происходит с 1,/re/
последующим и без него s//
:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
урожайность $'1bar\n2bar'
; то есть обе строки были обновлены, потому что номер строки 1
соответствует 1-й строке, а регулярное выражение /foo/
- конец диапазона - затем ищется только для начала на следующей строке. Следовательно, в этом случае выбираются обе строки, и s/foo/bar/
замена выполняется для обеих из них.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
терпит неудачу : с sed: first RE may not be empty
(BSD / macOS) и sed: -e expression #1, char 0: no previous regular expression
(GNU), потому что во время обработки 1-й строки (из-за номера строки, 1
начинающего диапазон), регулярное выражение еще не применялось, поэтому//
не относится ни к чему.
За исключением sed
специального 0,/re/
синтаксиса GNU , любой диапазон, начинающийся с номера строки, эффективно исключает использование //
.