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 i sed
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
i ex
na przykład mogą zrobić wiele z tego, co sed
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ściowe sed
, 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órych sed
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ę z y///
. Jeśli często g
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 razie y/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 :
POSIXLY_CORRECT
Zmienna jest mowa bo POSIX określa, że jeśli sed
napotka EOF podczas próby N
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żde sed
polecenie, które akceptuje dowolny parametr, rozumiane jest jako ograniczenie w \n
ewline w skrypcie. Więc polecenia ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... bardzo prawdopodobne jest nieprawidłowe działanie w zależności od sed
implementacji, która je czyta. Przenośne powinny być napisane:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
To samo odnosi się do r
, w
, t
, a
, i
, i c
(i ewentualnie kilka bardziej, że jestem zapominając w tej chwili) . W prawie każdym przypadku można je również napisać:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... gdzie nowa -e
instrukcja \n
xecution oznacza separator ewline. Więc tam, gdzie info
tekst GNU sugeruje, że tradycyjne sed
wdrożenie zmusiłoby cię do zrobienia :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... raczej powinno być ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... 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:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... który drukuje:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... 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ępuje h
stare miejsce. Bufory są następnie x
zmieniane, a s///
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
ot d
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ób sed
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 i n
ext dla uprzedzona - ponieważ postęp naprzód cyklu linii. Zamiast redundantnie implementować zamkniętą pętlę w pętli - ponieważ i tak sed
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ć:
sed 'H;1h;$!d;x;...'
... który zbierze cały plik lub spróbuje.
uwaga dodatkowa N
i zachowanie w ostatniej linii ...
chociaż nie mam dostępnych narzędzi do przetestowania, weź pod uwagę, że N
podczas czytania i edycji w miejscu zachowuje się inaczej, jeśli edytowany plik jest plikiem skryptu do następnego odczytu.