Czytanie całego pliku w przestrzeni wzorów jest przydatne do zastępowania nowych linii & c. i istnieje wiele przypadków zalecających następujące działania:
sed ':a;N;$!ba; [commands...]'
Jednak nie powiedzie się, jeśli dane wejściowe zawierają tylko jedną linię.
Na przykład, przy wprowadzaniu dwóch linii, każda linia podlega komendzie podstawienia:
$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt
Ale przy wprowadzaniu pojedynczego wiersza nie jest wykonywane podstawienie:
$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc
Jak napisać sed
polecenie, aby odczytać wszystkie dane wejściowe jednocześnie i nie mieć tego problemu?
sed -z
opcji GNU . Jeśli twój plik nie ma wartości null, będzie czytał do końca pliku! Znalezione z tego: stackoverflow.com/a/30049447/582917Odpowiedzi:
Istnieje wiele powodów, dla których wczytywanie całego pliku do obszaru wzorców może się nie powieść. Problem logiczny w pytaniu dotyczącym ostatniego wiersza jest powszechny. Jest to związane z
sed
cyklem linii - kiedy nie ma już linii ised
napotyka EOF, to przez nie przechodzi - przerywa przetwarzanie. I tak, jeśli jesteś na ostatniej linii i instruujesz,sed
aby zdobyć kolejną, to zatrzyma się na tym miejscu i nie będzie więcej robić.To powiedziawszy, jeśli naprawdę musisz wczytać cały plik do przestrzeni wzorców, prawdopodobnie i tak warto rozważyć inne narzędzie. Faktem jest, że
sed
jest to tytułowy edytor strumieniowy - jest przeznaczony do pracy z linią - lub logicznym blokiem danych - na raz.Istnieje wiele podobnych narzędzi, które są lepiej wyposażone do obsługi pełnych bloków plików.
ed
iex
na przykład mogą zrobić wiele z tego, cosed
można zrobić i przy podobnej składni - i wiele więcej poza tym - ale zamiast działać tylko na strumieniu wejściowym podczas przekształcania go na dane wyjściowesed
, zachowują również tymczasowe pliki kopii zapasowych w systemie plików . W razie potrzeby ich praca jest buforowana na dysk i nie poddają się gwałtownie na końcu pliku (i zwykle rzadziej implodują pod obciążeniem bufora) . Ponadto oferują wiele przydatnych funkcji, którychsed
nie ma - w rodzaju, które po prostu nie mają sensu w kontekście strumienia - takich jak znaczniki linii, cofanie, nazwane bufory, łączenie i inne.sed
podstawową siłą jest zdolność do przetwarzania danych natychmiast po ich odczytaniu - szybko, wydajnie i strumieniowo. Kiedy wycierasz plik, wyrzucasz go i masz tendencję do napotkania trudności z marginesami, takich jak problem z ostatnią linią, o którym wspomniałeś, przepełnienia bufora i beznadziejna wydajność - ponieważ analizowane dane wydłużają czas przetwarzania wyrażeń regularnych podczas wyliczania dopasowań rośnie wykładniczo .Nawiasem mówiąc, jeśli chodzi o ten ostatni punkt: chociaż rozumiem, że przykładowy
s/a/A/g
przypadek jest prawdopodobnie naiwnym przykładem i prawdopodobnie nie jest rzeczywistym skryptem, który chcesz zebrać w danych wejściowych, być może warto poświęcić chwilę na zapoznanie się zy///
. Jeśli częstog
lobalnie zastępujesz jedną postać inną,y
może to być bardzo przydatne. Jest to transformacja w przeciwieństwie do podstawienia i jest znacznie szybsza, ponieważ nie oznacza wyrażenia regularnego. Ten ostatni punkt może również być przydatny przy próbie zachowania i powtórzenia pustych//
adresów, ponieważ nie wpływa na nie, ale może być przez nie zmieniony. W każdym raziey/a/A/
jest to prostszy sposób na osiągnięcie tego samego - i możliwe są także wymiany:y/aA/Aa/
które zamieniają wszystkie wielkie / małe litery jak na linii dla siebie.Należy również pamiętać, że opisywane zachowanie tak naprawdę nie jest tym, co powinno się wydarzyć.
Z GNU
info sed
w sekcji WSPÓLNIE ZGŁASZANE BŁĘDY :N
polecenie w ostatnim wierszuWiększość wersji
sed
kończy pracę bez drukowania niczego, gdyN
polecenie jest wydawane w ostatnim wierszu pliku. GNUsed
drukuje przestrzeń wzorca przed wyjściem, chyba że oczywiście określono-n
przełącznik poleceń. Ten wybór jest zgodny z projektem.Na przykład zachowanie
sed N foo bar
zależy od tego, czy foo ma parzystą czy nieparzystą liczbę linii. Lub, podczas pisania skryptu do odczytu kilku kolejnych wierszy po dopasowaniu wzorca, tradycyjne implementacjesed
zmusiłyby cię do napisania czegoś podobnego/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
zamiast po prostu/foo/{ N;N;N;N;N;N;N;N;N; }
.W każdym razie najprostszym obejściem jest użycie
$d;N
w skryptach opartych na tradycyjnym zachowaniu lub ustawieniePOSIXLY_CORRECT
zmiennej na niepustą wartość.POSIXLY_CORRECT
Zmienna jest mowa bo POSIX określa, że jeślised
napotka EOF podczas próbyN
powinno wyjść bez wyjścia, ale w wersji GNU świadomie zrywa z normą w tym przypadku. Zauważ też, że nawet jeśli zachowanie jest uzasadnione powyżej założenia, że przypadek błędu dotyczy edycji strumieniowej - a nie umieszczania całego pliku w pamięci.W standardowych definiuje
N
„S zachowanie sposób:N
Dołącz następny wiersz danych wejściowych, pomniejszając
\n
końcową ewlinię, do przestrzeni wzoru, używając osadzonej\n
ewline, aby oddzielić dołączony materiał od materiału oryginalnego. Zauważ, że bieżący numer linii zmienia się.Jeśli nie jest dostępny następny wiersz danych wejściowych,
N
czasownik polecenia rozgałęzia się do końca skryptu i kończy pracę bez rozpoczynania nowego cyklu lub kopiowania przestrzeni wzorców na standardowe wyjście.W tej notatce pokazano kilka innych GNU-izmów - w szczególności użycie
:
etykiety,b
ranch i{
nawiasów kontekstowych funkcji}
. Zasadniczo każdesed
polecenie, które akceptuje dowolny parametr, rozumiane jest jako ograniczenie w\n
ewline w skrypcie. Więc polecenia ...... bardzo prawdopodobne jest nieprawidłowe działanie w zależności od
sed
implementacji, która je czyta. Przenośne powinny być napisane:To samo odnosi się do
r
,w
,t
,a
,i
, ic
(i ewentualnie kilka bardziej, że jestem zapominając w tej chwili) . W prawie każdym przypadku można je również napisać:... gdzie nowa
-e
instrukcja\n
xecution oznacza separator ewline. Więc tam, gdzieinfo
tekst GNU sugeruje, że tradycyjnesed
wdrożenie zmusiłoby cię do zrobienia :... raczej powinno być ...
... oczywiście to też nie jest prawda. Pisanie w ten sposób scenariusza jest trochę głupie. Istnieją znacznie prostsze sposoby robienia tego samego, na przykład:
... który drukuje:
... ponieważ
t
polecenie est - podobnie jak większośćsed
poleceń - zależy od cyklu linii w celu odświeżenia rejestru zwrotnego i tutaj cykl linii może wykonać większość pracy. Jest to kolejny kompromis, którego dokonujesz, gdy kopiujesz plik - cykl linii nigdy się nie odświeża i tak wiele testów zachowuje się nienormalnie.Powyższe polecenie nie ryzykuje przekroczenia zakresu danych wejściowych, ponieważ wykonuje tylko kilka prostych testów, aby zweryfikować, co czyta podczas czytania. W przypadku
H
starego wszystkie wiersze są dodawane do miejsca wstrzymania, ale jeśli linia jest zgodna/foo/
, zastępujeh
stare miejsce. Bufory są następniex
zmieniane, as///
próba uwarunkowania warunkowego jest podejmowana, jeśli zawartość bufora jest zgodna z//
ostatnim adresowanym wzorcem. Innymi słowy,//s/\n/&/3p
próbuje zastąpić trzeci znak nowej linii w przestrzeni wstrzymania i wydrukować wyniki, jeśli przestrzeń wstrzymania jest obecnie zgodna/foo/
. Jeśli to sięt
powiedzie, skrypt rozgałęzia się na etykietęn
otd
elete - co powoduje,l
że skrypt kończy pracę.W przypadku, gdy oba
/foo/
i trzeci nowej linii, nie mogą być dopasowane razem w przestrzeni utrzymywania jednak następnie//!g
zastąpi bufor Jeżeli/foo/
nie jest dopasowany, lub, jeśli jest dopasowany, to zastąpić buforem jeśli\n
ewline nie jest dopasowany (w miejsce/foo/
z sama) . Ten mały, subtelny test zapobiega niepotrzebnemu zapełnianiu się bufora przez długie odcinki „nie”/foo/
i zapewnia, że proces pozostanie bezproblemowy, ponieważ dane wejściowe się nie nakładają. W przypadku braku/foo/
lub//s/\n/&/3p
awarii bufory są ponownie zamieniane, a każda linia oprócz ostatniej jest tam usuwana.Ta ostatnia - ostatnia linia
$!d
- jest prostym pokazem, w jaki sposóbsed
można wykonać skrypt odgórny, aby łatwo obsługiwać wiele spraw. Kiedy twoją ogólną metodą jest wycinanie niechcianych przypadków, zaczynając od najbardziej ogólnych i pracując w kierunku najbardziej specyficznych, wówczas przypadki brzegowe można łatwiej obsłużyć, ponieważ mogą one po prostu spaść do końca skryptu z innymi poszukiwanymi danymi i kiedy to wszystko otula cię tylko danymi, których potrzebujesz. Konieczność pobrania takich przypadków brzegowych z zamkniętej pętli może być jednak znacznie trudniejsza.I oto ostatnia rzecz, którą muszę powiedzieć: jeśli naprawdę musisz pobrać cały plik, możesz znieść nieco mniej pracy, polegając na cyklu linii, aby to zrobić za Ciebie. Zazwyczaj należy użyć
N
ext in
ext dla uprzedzona - ponieważ postęp naprzód cyklu linii. Zamiast redundantnie implementować zamkniętą pętlę w pętli - ponieważ i taksed
cykl linii jest po prostu zwykłą pętlą odczytu - jeśli Twoim celem jest tylko gromadzenie danych wejściowych bez rozróżnienia, prawdopodobnie łatwiej jest zrobić:... który zbierze cały plik lub spróbuje.
uwaga dodatkowa
N
i zachowanie w ostatniej linii ...źródło
H
pierwszym miejscu jest piękne.:a;$!{N;ba}
jak wspomniałem powyżej - łatwiej jest używać standardowej formy na dłuższą metę, gdy próbujesz uruchomić wyrażenia regularne na nieznanych systemach. Ale tak naprawdę nie to miałem na myśli: wdrażasz zamkniętą pętlę - nie możesz tak łatwo dostać się do środka, kiedy chcesz, zamiast tego, rozgałęziając - przycinając niechciane dane - i pozwalając na cykl. To jak odgórna rzecz - wszystko, cosed
robi, jest bezpośrednim wynikiem tego, co właśnie zrobiono. Może widzisz to inaczej - ale jeśli spróbujesz, skrypt może okazać się łatwiejszy.Nie udaje się, ponieważ
N
polecenie pojawia się przed dopasowaniem wzorca$!
(nie ostatni wiersz) i sed kończy pracę przed wykonaniem jakiejkolwiek pracy:Można to łatwo naprawić, aby działało również z wprowadzaniem jednowierszowym (a nawet by było bardziej jasne w każdym przypadku), po prostu grupując polecenia
N
ib
po wzorcu:Działa w następujący sposób:
:a
utwórz etykietę o nazwie „a”$!
jeśli nie ostatnia linia, toN
dołącz następny wiersz do obszaru wzorów (lub wyjdź, jeśli nie ma następnego wiersza) iba
rozgałęź (przejdź do) etykietę „a”Niestety nie jest przenośny (ponieważ opiera się na rozszerzeniach GNU), ale następująca alternatywa (sugerowana przez @mikeserv) jest przenośna:
źródło
:a;N;$!ba;
.