Jaki jest sens używania wielu wykrzykników w sed?

12

Dokumentacja POSIX sed mówi:

Funkcja może być poprzedzona jednym lub więcej „!” znaki, w którym to przypadku należy zastosować funkcję, jeśli adresy nie wybierają przestrzeni wzorca. Zero lub więcej <blank> znaków zostanie zaakceptowanych przed pierwszym „!” postać. Nie jest określone, czy znaki <blank> mogą występować po znaku „!” znak, a zgodne aplikacje nie powinny występować po znaku „!” znak ze znakami <blank>.

Tak więc, przy pomocy dowolnego POSIX sed, możemy:

sed -e '/pattern/!d' file

To tak samo jak pisanie:

sed -e '/pattern/!!d' file

A !!!di nod wykrzykniki są nadal będzie dobrze (testowane z trzech sedwersji z scheda Biblioteka szablonów ). Nie widzę żadnej korzyści między wielokrotnością zamiast jednego wykrzyknika.

Dlaczego specyfikacja zezwala na tę składnię i jak jest przydatna w aplikacjach w świecie rzeczywistym?


Wygląda na to, że GNU sed nie jest w tym przypadku zgodny, będzie narzekać, jeśli użyjemy wielu wykrzykników:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s
Cuonglm
źródło
2
FWIW: Na OpenBSD !działa jak przełącznik, /pattern/!!jest taki sam jak /pattern/i /pattern/!!!jest taki sam jak /pattern/!. Na FreeBSD wiele !jest takich samych jak jeden.
lcd047
2
Wiele rzeczy w specyfikacji polega na tym, że sedmożna generować skrypty . Biorąc pod uwagę POSIX sed, napisanie sedskryptu powinno być naprawdę prostą sprawą . I tak, jeśli masz jakiś wyzwalacz dla jakiegoś przypadku, który powinien oznaczać adres !niegodny tego, czym była twoja akcja, możesz nawet uruchomić to wiele razy dla tego samego i nadal wychodzić z tymi samymi rezultatami.
mikeserv
@cuonglm Nie, tylko FreeBSD jest. GNU, OpenBSD i NetBSD sednie są.
lcd047,
@ lcd047: tak, oczywiście. Przepraszam za mój zły język angielski. Mam na myśli, że to nie jest zgodne, prawda? Dobrze to wiedzieć. Ale głównym punktem mojego pytania jest to, w jaki sposób ta składnia może być przydatna w świecie rzeczywistym z POSIX-em sed?
cuonglm
1
FWIW: naprawiono to w OpenBSD-current.
lcd047

Odpowiedzi:

5

sedInterfejs API jest prymitywny - i to z założenia. Przynajmniej pozostał prymitywny z założenia - nie wiem, czy został zaprojektowany pierwotnie od samego początku. W większości przypadków napisanie sedskryptu, który po uruchomieniu wygeneruje inny sedskrypt, jest w rzeczywistości prostą sprawą. sedjest bardzo często stosowany w ten sposób przez preprocesory makr, takie jak m4i / lub make.

(Poniżej znajduje się wysoce hipotetyczny przypadek użycia: jest to problem zaprojektowany w celu dopasowania do rozwiązania. Jeśli wydaje ci się, że jest to rozciągnięcie, prawdopodobnie jest tak, ponieważ tak jest, ale niekoniecznie czyni go mniej ważnym.)


Rozważ następujący plik wejściowy:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

Gdybyśmy chcieli napisać sedskrypt, który dołączałby słowo- literę do ogona każdego odpowiedniego słowa w powyższym pliku wejściowym tylko wtedy, gdyby można go było znaleźć w wierszu w odpowiednim kontekście , i chcieliśmy to zrobić tak efektywnie, jak to możliwe ( co powinno być naszym celem, na przykład podczas operacji kompilacji) , powinniśmy raczej unikać stosowania /wyrażeń regularnych w /jak największym stopniu.

Jedną z rzeczy, które moglibyśmy zrobić, jest wstępna edycja pliku w naszym systemie i nigdy nie wywoływać sedw ogóle podczas kompilacji. Ale jeśli którekolwiek z tych słów w pliku powinno lub nie powinno być uwzględnione w oparciu o ustawienia lokalne i / lub opcje czasu kompilacji, to prawdopodobnie nie byłoby pożądaną alternatywą.

Inną rzeczą, którą moglibyśmy zrobić, to przetworzenie pliku teraz na wyrażenie regularne. Możemy stworzyć - i dołączyć do naszej kompilacji - sedskrypt, który może wprowadzać zmiany zgodnie z numerem linii - co w dłuższej perspektywie jest zazwyczaj znacznie wydajniejszą trasą.

Na przykład:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'

... który zapisuje dane wyjściowe w postaci sedskryptu i który wygląda jak ...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q

Gdy dane wyjściowe są zapisywane w wykonywalnym pliku tekstowym na moim komputerze o nazwie ./bang.sedi działają tak ./bang.sed ./infile, dane wyjściowe są następujące:

camel-case
upper-case
lower-case

Teraz możesz mnie zapytać ... Dlaczego miałbym to zrobić? Dlaczego nie miałbym po prostu dopasowywać grepzapałek? Kto w ogóle używa futerału na wielbłądy? I na każde pytanie mogłem tylko odpowiedzieć, nie mam pojęcia ... ponieważ nie mam. Przed przeczytaniem tego pytania nigdy osobiście nie zauważyłem multi-! wymaganie analizy w specyfikacji - myślę, że to całkiem fajny haczyk.

Multi-! rzecz nie od razu dla mnie sensu, choć - znaczna część sedspecyfikacji jest nastawiona prostu analizowany i po prostu generowanych sed skryptów. Prawdopodobnie znajdziesz w tym kontekście wymagane \nograniczniki ewline, które [wr:bt{]mają o wiele więcej sensu, a jeśli będziesz pamiętać o tym pomyśle, możesz lepiej zrozumieć inne aspekty specyfikacji - (takie jak :brak akceptacji adresów i qodmowa zaakceptować więcej niż 1) .

W przykładzie powyżej piszę z pewną formę sedskryptu, który może tylko kiedykolwiek być odczytywane raz. Jeśli przyjrzysz się temu uważnie, zauważysz, że podczas sedodczytywania pliku edycji przechodzi on od jednego bloku poleceń do następnego - nigdy nie rozgałęzia się ani nie kończy skryptu edycji, dopóki nie przejdzie do końca z plikiem edycji.

Uważam to za multi-! adresy mogą być bardziej przydatne w tym kontekście niż w niektórych innych, ale szczerze mówiąc, nie mogę wymyślić jednego przypadku, w którym mógłbym bardzo dobrze je wykorzystać - i to sedbardzo często. Myślę też, że warto zauważyć, że sedoba GNU / BSD nie radzą sobie z tym, jak podano - prawdopodobnie nie jest to aspekt specyfikacji, który jest bardzo pożądany, więc jeśli implementacja przeoczy ją, wątpię bardzo poważnie, że ich błędy @ box będą cierpieć strasznie w rezultacie.

To powiedziawszy, brak obsługi tego, jak określono, jest błędem dla dowolnej implementacji, która udaje zgodność, dlatego myślę, że należy wysłać wiadomość e-mail do odpowiednich skrzynek programistycznych i zamierzam to zrobić, jeśli nie.

mikeserv
źródło
1
Zostało to naprawione w OpenBSD-current.
lcd047
1
Wiele !zostanie usuniętych w następnej specyfikacji , co się tutaj dzieje!
cuonglm
@cuonglm - chyba za późno. może byłem bliżej znaku niż myślałem.
mikeserv
@cuonglm - cóż, ok, ale co to znaczy ... Akceptowane jako Oznaczone ?
mikeserv
1
@mikeserv: odpowiedź wyjaśniła mój cud i dała mi inny widok z sed API. Ma to dla mnie sens!
cuonglm,