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