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, kshlub zshprzyjmuje się jako powłoki.
sedTylko 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/( rereprezentuje 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 reod 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 redo kolejnych linii; innymi słowy: nie wykryje to pierwszego wystąpienia redopasowania, 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.
sedzapewnia 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, sedtakie 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, reczy 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 swywołaniu; w obu przypadkach regex foojest domyślnie ponownie wykorzystywany, co pozwala nam nie musieć go powielać, co czyni zarówno krótszy, jak i łatwiejszy do utrzymania kod.
POSIX sedpotrzebuje 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 -eopcji jest alternatywą dla użycia rzeczywistych znaków nowej linii: zakończ każdą -eczęść skryptu tam, gdzie normalnie musiałaby iść nowa linia.
1 s/foo/bar/zastępuje tylko foow 1. linii, jeśli ją tam znajdziesz. Jeśli tak, trozgałęzia się do końca skryptu (pomija pozostałe polecenia w wierszu). ( tFunkcja rozgałęzia się na etykietę tylko wtedy, gdy ostatnie swywoł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 1pasuje 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 1rozpoczynającego zakres) nie zastosowano jeszcze wyrażenia regularnego, więc//nie odnosi się do niczego.
Z wyjątkiem sedspecjalnej 0,/re/składni GNU , każdy zakres rozpoczynający się od numeru linii skutecznie wyklucza użycie //.