Огляд з багатьох корисних існуючих відповідей , доповнених з поясненнями :
У прикладах тут використовується спрощений випадок використання: замініть слово "foo" на "bar" лише в першому рядку, що відповідає.
Завдяки використанню ANSI C-рядків в лапках ( $'...'
) , щоб забезпечити вибірки вхідних ліній, bash
, ksh
або zsh
передбачаються в якості оболонки.
GNU sed
:
Повідомлення Бена Гофштейна показує нам, що 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
якщо це трапляється на першому рядку (див. Вище), потрібне спеціальне оброблення для першого рядка .
Відповідь Михайловича згадує техніку, наведену тут на конкретному прикладі:
$ 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 - й лінії , або будь-який інший.
Підходи без діапазону
Відповідь Potong в демонструє петльові методи , які обходять необхідність в діапазоні ; оскільки він використовує синтаксис 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 , будь-який діапазон, що починається з номера рядка, фактично виключає використання //
.