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