Was bringt es, in sed mehrere Ausrufezeichen zu verwenden?

12

POSIX sed Dokumentation sagte:

Einer Funktion können ein oder mehrere '!' Zeichen. In diesem Fall wird die Funktion angewendet, wenn die Adressen den Musterraum nicht auswählen. Vor dem ersten '!' Werden null oder mehr <leere> Zeichen akzeptiert. Charakter. Es ist nicht festgelegt, ob <leere> Zeichen nach einem '!' Zeichen und konforme Anträge dürfen nicht nach einem '!' Zeichen mit <Leerzeichen>.

Mit jeder POSIX-Version können wir also:

sed -e '/pattern/!d' file

Es ist das gleiche wie beim Schreiben:

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

Und !!!dund nAusrufezeichen sind immer noch in Ordnung (Getestet mit drei sedVersionen von Erbstück Werkzeugkasten ). Ich sehe keinen Vorteil zwischen mehreren anstelle eines Ausrufezeichens.

Warum hat die Spezifikation diese Syntax zugelassen und wie nützlich ist sie in der Praxis?


Es scheint, dass GNU sed in diesem Fall nicht kompatibel ist. Es wird sich beschweren, wenn wir mehrere Ausrufe verwenden:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s
cuonglm
quelle
2
FWIW: Unter OpenBSD !fungiert es als Toggle, /pattern/!!ist dasselbe wie /pattern/und /pattern/!!!ist dasselbe wie /pattern/!. Unter FreeBSD sind mehrere !gleich einem einzelnen.
lcd047
2
Der Sinn vieler Dinge in der Spezifikation ist, dass sedSkripte generiert werden können . Bei einem POSIX sedsollte es relativ einfach sein, ein Skript zu schreiben sed. Wenn Sie also für einen bestimmten Fall einen Auslöser hatten, der eine Adresse kennzeichnen sollte, die !Ihrer Aktion nicht würdig ist, können Sie diesen sogar mehrere Male für denselben auslösen und trotzdem die gleichen Ergebnisse erzielen.
mikeserv
@cuonglm Nein, nur FreeBSD ist. Die GNU-, OpenBSD- und NetBSD- sedVersionen sind es nicht.
lcd047
@ lcd047: ja natürlich. Entschuldigung für mein schlechtes Englisch. Ich meine, es ist nicht konform, oder? Das ist gut zu wissen. Aber der Hauptpunkt meiner Frage ist, wie diese Syntax in der realen Welt mit POSIX sed nützlich sein kann.
Donnerstag,
1
FWIW: Ein Fix dafür wurde in OpenBSD-current geschrieben.
lcd047

Antworten:

5

sedDie API von ist primitiv - und dies ist beabsichtigt. Zumindest ist es von Grund auf primitiv geblieben - ob es von Anfang an primitiv entworfen wurde, kann ich nicht sagen. In den meisten Fällen ist das Schreiben eines sedSkripts, das beim Ausführen ein anderes sedSkript ausgibt , in der Tat eine einfache Angelegenheit. sedwird sehr oft von Makro-Präprozessoren wie m4und / oder auf diese Weise angewendet make.

(Was folgt, ist ein sehr hypothetischer Anwendungsfall: Es ist ein Problem, das speziell für eine Lösung entwickelt wurde. Wenn es sich für Sie wie eine Ausdehnung anfühlt, liegt das wahrscheinlich daran, aber das macht es nicht unbedingt weniger gültig.)


Betrachten Sie die folgende Eingabedatei:

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

Wollten wir ein sedSkript schreiben, das das Wort- case nur dann an das Ende jedes entsprechenden Wortes in der obigen Eingabedatei anfügt , wenn es in einer Zeile in einem geeigneten Kontext gefunden werden kann , und wollten dies so effizient wie möglich tun (/ Da dies unser Ziel sein sollte, zum Beispiel während eines Kompiliervorgangs, sollten wir es vorziehen, Regexps /so weit wie möglich zu vermeiden .

Eine Sache, die wir tun könnten, ist, die Datei auf unserem System vorab zu bearbeiten und sie sedwährend der Kompilierung niemals aufzurufen . Wenn jedoch eines dieser Wörter in der Datei auf der Grundlage lokaler Einstellungen und / oder Optionen zur Kompilierungszeit enthalten sein sollte oder nicht, wäre dies wahrscheinlich keine wünschenswerte Alternative.

Eine andere Möglichkeit besteht darin, die Datei jetzt gegen reguläre Ausdrücke zu verarbeiten. Wir können ein sedSkript erstellen - und in unsere Zusammenstellung aufnehmen -, das Bearbeitungen entsprechend der Zeilennummer vornehmen kann - was auf lange Sicht in der Regel eine weitaus effizientere Route darstellt.

Beispielsweise:

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|'

... der die Ausgabe in Form eines sedSkripts schreibt und so aussieht ...

#!/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

Wenn diese Ausgabe in einer ausführbaren Textdatei auf meinem Computer mit dem Namen gespeichert wird ./bang.sedund wie ./bang.sed ./infilefolgt ausgeführt wird , lautet die Ausgabe:

camel-case
upper-case
lower-case

Jetzt fragst du mich vielleicht ... Warum sollte ich das tun wollen? Warum sollte ich nicht einfach grepdie Streichhölzer ankern ? Wer benutzt schon Camel-Case? Und auf jede Frage konnte ich nur antworten, ich habe keine Ahnung ... weil ich es nicht tue. Bevor ich diese Frage gelesen habe, war mir das Multi-! Parsing-Anforderung in der Spezifikation - Ich denke, es ist ein ziemlich ordentlicher Fang.

Die Multi-! Für mich ergab das jedoch sofort einen Sinn - ein Großteil der sedSpezifikation ist auf einfach analysierte und einfach generierte sed Skripte ausgerichtet. Sie werden wahrscheinlich die erforderlichen \newline-Begrenzer finden, [wr:bt{]um in diesem Kontext viel sinnvoller zu sein, und wenn Sie diese Idee berücksichtigen, könnten Sie einige andere Aspekte der Spezifikation besser verstehen - wie das :Akzeptieren von Adressen und das qAblehnen von Adressen akzeptiere mehr als 1) .

Im obigen Beispiel schreibe ich eine bestimmte Form von sedSkript, die immer nur einmal gelesen werden kann. Wenn Sie es sich genauer ansehen, werden Sie feststellen, dass es beim sedLesen der Bearbeitungsdatei von einem Befehlsblock zum nächsten weitergeht - es verzweigt nie von seinem Bearbeitungsskript weg oder vervollständigt es, bis es vollständig mit seiner Bearbeitungsdatei fertig ist.

Ich halte das für multi-! Adressen mögen in diesem Zusammenhang nützlicher sein als in einigen anderen, aber ehrlich gesagt, fällt mir kein einziger Fall ein, in dem ich ihn möglicherweise sehr gut genutzt hätte - und ich sedsehr viel. Ich halte es auch für bemerkenswert, dass GNU / BSD sedbeide nicht wie angegeben damit umgehen - dies ist wahrscheinlich kein Aspekt der Spezifikation, der stark nachgefragt wird, und wenn eine Implementierung es übersieht, bezweifle ich, dass ihre bugs @ box darunter leiden wird schrecklich als Ergebnis.

Das heißt, die Nichtbeachtung dieser Vorgaben ist ein Fehler für jede Implementierung, die sich als konform ausgibt. Ich denke, hier ist es angebracht, eine E-Mail an die entsprechenden Entwickler-Boxen zu senden, und ich beabsichtige, dies zu tun, wenn Sie dies nicht tun.

mikeserv
quelle
1
Es ist jetzt in OpenBSD-current behoben.
lcd047,
1
Mehrere !werden in der nächsten Spezifikation entfernt , was hier los ist!
Dienstag,
@ Cuonglm - zu wenig zu spät, denke ich. Vielleicht war ich näher an der Marke als ich dachte.
mikeserv
@cuonglm - na ja, ok, aber was bedeutet das ... Akzeptiert als "Markiert" überhaupt?
mikeserv
1
@mikeserv: die antwort hat mein wunder erklärt und mir eine andere ansicht mit sed API gegeben. Es macht Sinn für mich!
Dienstag,