Przegląd z wielu pomocnych istniejących odpowiedzi , uzupełnione z wyjaśnieniami :
Przykłady tutaj wykorzystują uproszczony przypadek użycia: zamień słowo „foo” na „bar” tylko w pierwszym pasującym wierszu.
Dzięki zastosowaniu ANSI C cudzysłowach ( $'...'
) dostarczenie próbki linii wejściowych bash
, ksh
lub zsh
przyjmuje się jako powłoki.
sed
Tylko GNU :
Ben Hoffstein za anwswer pokazuje nam, że GNU przewiduje się rozszerzenie do specyfikacji POSIX nased
który umożliwia następujące formy 2-adres : 0,/re/
( re
reprezentuje dowolne wyrażenie regularne tutaj).
0,/re/
pozwala dopasować wyrażenie regularne również w pierwszej linii . Innymi słowy: taki adres utworzy zakres od 1. linii do linii włącznie, która pasuje re
- niezależnie re
od tego, czy występuje w 1. linii, czy w dowolnej kolejnej linii.
- Porównaj to z formularzem zgodnym z POSIX
1,/re/
, który tworzy zakres, który pasuje od 1. linii do linii zawierającej, która pasuje re
do kolejnych linii; innymi słowy: nie wykryje to pierwszego wystąpienia re
dopasowania, jeśli zdarzy się ono w pierwszej linii, a także zapobiegnie użyciu skrótu//
do ponownego użycia ostatnio używanego wyrażenia regularnego (patrz następny punkt). 1
Jeśli połączysz 0,/re/
adres z wywołaniem s/.../.../
(podstawienia), które używa tego samego wyrażenia regularnego, twoje polecenie skutecznie wykona podstawienie tylko w pierwszym pasującym wierszu re
.
sed
zapewnia wygodny skrót do ponownego wykorzystania ostatnio stosowane wyrażenia regularnego : AN pusty parę ogranicznika,//
.
$ 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
Tylko funkcje POSIX, sed
takie jak BSD (macOS)sed
(będą również działać z GNU sed
):
Ponieważ 0,/re/
nie można go użyć, a formularz 1,/re/
nie wykryje, re
czy zdarzy się na pierwszej linii (patrz wyżej), wymagana jest specjalna obsługa pierwszej linii .
Odpowiedź MikhailVS wymienia technikę, podając tutaj konkretny przykład:
$ 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
Uwaga:
Pusty //
skrót wyrażenia regularnego jest tutaj stosowany dwa razy: raz dla punktu końcowego zakresu i raz w s
wywołaniu; w obu przypadkach regex foo
jest domyślnie ponownie wykorzystywany, co pozwala nam nie musieć go powielać, co czyni zarówno krótszy, jak i łatwiejszy do utrzymania kod.
POSIX sed
potrzebuje rzeczywistych znaków nowej linii po określonych funkcjach, takich jak nazwa etykiety lub nawet jej pominięcie, jak ma to miejsce w tym przypadku t
; strategiczne podzielenie skryptu na wiele -e
opcji jest alternatywą dla użycia rzeczywistych znaków nowej linii: zakończ każdą -e
część skryptu tam, gdzie normalnie musiałaby iść nowa linia.
1 s/foo/bar/
zastępuje tylko foo
w 1. linii, jeśli ją tam znajdziesz. Jeśli tak, t
rozgałęzia się do końca skryptu (pomija pozostałe polecenia w wierszu). ( t
Funkcja rozgałęzia się na etykietę tylko wtedy, gdy ostatnie s
wywołanie dokonało rzeczywistego podstawienia; przy braku etykiety, jak w tym przypadku, koniec skryptu jest rozgałęziony).
Gdy tak się stanie, adres zakresu 1,//
, który zwykle znajduje pierwsze wystąpienie, zaczynając od linii 2 , nie będzie pasował, a zakres nie zostanie przetworzony, ponieważ adres jest obliczany, gdy bieżąca linia już jest 2
.
I odwrotnie, jeśli nie ma dopasowania w 1. linii, 1,//
zostanie wprowadzone i znajdzie prawdziwe pierwsze dopasowanie.
Efekt netto jest taki sam jak z GNU sed
„s 0,/re/
: tylko pierwsze wystąpienie otrzymuje, czy występuje on na 1. linii lub dowolny inny.
Podejścia poza zasięgiem
odpowiedź potonga pokazuje techniki pętli , które omijają potrzebę zasięgu ; ponieważ używa składni GNU sed
, oto odpowiedniki zgodne z POSIX :
Technika pętli 1: przy pierwszym dopasowaniu wykonaj podstawienie, a następnie wejdź do pętli, która po prostu drukuje pozostałe linie bez zmian :
$ 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
Technika pętli 2, tylko dla małych plików : wczytaj całą zawartość do pamięci, a następnie wykonaj na niej pojedyncze podstawienie .
$ 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 podaje przykłady tego, co dzieje się 1,/re/
z następującymi i później s//
:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
plonami $'1bar\n2bar'
; tzn. obie linie zostały zaktualizowane, ponieważ numer linii 1
pasuje do 1. linii, a wyrażenie regularne /foo/
- koniec zakresu - jest wtedy szukane tylko od następnego wiersza. Dlatego w tym przypadku wybiera się obie linie, a s/foo/bar/
zastępowanie odbywa się na obu z nich.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
kończy się niepowodzeniem : w przypadku sed: first RE may not be empty
(BSD / macOS) i sed: -e expression #1, char 0: no previous regular expression
(GNU), ponieważ w czasie przetwarzania pierwszego wiersza (z powodu numeru wiersza 1
rozpoczynającego zakres) nie zastosowano jeszcze wyrażenia regularnego, więc//
nie odnosi się do niczego.
Z wyjątkiem sed
specjalnej 0,/re/
składni GNU , każdy zakres rozpoczynający się od numeru linii skutecznie wyklucza użycie //
.