Das Konzept von 'Hold Space' und 'Pattern Space' in sed

86

Ich bin verwirrt von den beiden Konzepten in sed: Hold Space und Pattern Space. Kann jemand helfen, sie zu erklären?

Hier ist ein Ausschnitt aus dem Handbuch:

h H    Copy/append pattern space to hold space.
g G    Copy/append hold space to pattern space.

n N    Read/append the next line of input into the pattern space.

Diese sechs Befehle verwirren mich wirklich.

ChenQi
quelle
4
Probieren Sie es selbst aus:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
Choroba
4
Sei nicht verwirrt, benutze sie einfach nicht. Für alles andere als einfache Ersetzungen in einer einzelnen Zeile sollten Sie awk verwenden, nicht sed. Laderäume, Musterräume und 95% der sed-Sprachkonstrukte wurden vor awk erfunden, als es keine bessere Alternative gab. Sie wurden obsolet, sobald awk Mitte der 1970er Jahre erfunden wurde, und werden heute nur noch von Menschen am Leben erhalten, die es genießen, Probleme mit seds arkaner Syntax zu lösen, anstatt es einfach und vorsichtig in awk zu tun. Wenn Sie in sed mehr als s, g und p (mit -n) verwenden, verwenden Sie mit ziemlicher Sicherheit das falsche Werkzeug.
Ed Morton
26
Morton awk arbeitet mit strukturierten Daten (jede Zeile hat dieselbe Struktur). Sed soll mit zufälligen Rohdaten arbeiten. Sie können also nicht einfach awk anstelle von sed verwenden.
Pithikos
5
Ich empfehle dringend zu lesen info sed. Es ist viel detaillierter als die bloße Manpage.
Fernando Basso
4
Ich stimme Pithikos zu. Ich ging die Gasse entlang wie Morton und stellte mir dieselbe Frage wie Morton. Allerdings konnte ich sed noch nicht so einfach entlassen.
Eigenfeld

Antworten:

111

Wenn Sed liest eine Datei Zeile für Zeile, die Zeile , die in den eingeführt ist derzeit gelesen wurde Musterpuffer (Musterraum). Der Musterpuffer ist wie der temporäre Puffer, das Notizbuch, auf dem die aktuellen Informationen gespeichert sind. Wenn Sie sed anweisen, zu drucken, wird der Musterpuffer gedruckt.

Hold Buffer / Hold Space ist wie ein Langzeitspeicher, sodass Sie etwas abfangen, speichern und später wiederverwenden können, wenn sed eine andere Zeile verarbeitet. Sie verarbeiten den Haltebereich nicht direkt, sondern müssen ihn kopieren oder an den Musterbereich anhängen, wenn Sie etwas damit tun möchten. Beispielsweise pdruckt der Druckbefehl nur den Musterbereich. Ebenso sarbeitet auf dem Musterraum.

Hier ist ein Beispiel:

sed -n '1!G;h;$p'

(Die Option -n unterdrückt das automatische Drucken von Zeilen.)

Hier gibt es drei Befehle : 1!G, hund $p. 1!Ghat eine Adresse 1(erste Zeile), aber das !bedeutet, dass der Befehl überall ausgeführt wird, außer in der ersten Zeile. $pAuf der anderen Seite wird nur in der letzten Zeile ausgeführt. Was also passiert, ist Folgendes:

  1. Die erste Zeile wird gelesen und automatisch in den Musterbereich eingefügt
  2. In der ersten Zeile wird der erste Befehl nicht ausgeführt. hkopiert die erste Zeile in den Haltebereich .
  3. Jetzt ersetzt die zweite Zeile alles, was sich im Musterraum befand
  4. In der zweiten Zeile wird zuerst ausgeführt G, wobei der Inhalt des Haltepuffers an den Musterpuffer angehängt und durch eine neue Zeile getrennt wird. Der Musterbereich enthält jetzt die zweite Zeile, eine neue Zeile und die erste Zeile.
  5. Anschließend hfügt der Befehl den verketteten Inhalt des Musterpuffers in den Haltebereich ein, der nun die umgekehrten Zeilen zwei und eins enthält.
  6. Wir fahren mit Zeile drei fort - gehen Sie zu Punkt (3) oben.

Nachdem die letzte Zeile gelesen und der Haltebereich (der alle vorherigen Zeilen in umgekehrter Reihenfolge enthält) an den Musterbereich angehängt wurde, wird der Musterbereich mit gedruckt p. Wie Sie vermutet haben, macht das Obige genau das, was der tacBefehl tut - druckt die Datei in umgekehrter Reihenfolge.

Januar
quelle
3
Funktioniert die Option G und h wie "Ausschneiden und Anhängen"? Es sieht nicht nach "Kopieren und Anhängen" aus.
Lächeln
Was hängt mit dem Muster- und Haltebereich zusammen, wenn verschachtelte Befehle (geschweifte Klammern) verwendet werden? '195,210{/add/p}'… Ist es möglich, die letzte Zeile einer Gruppe von Zeilen zu extrahieren, die an einem Muster beteiligt sind?
Sandburg
17

@ Ed Morton: Ich stimme dir hier nicht zu. Ich fand es sedsehr nützlich und einfach (sobald Sie das Konzept des Musters verstanden haben und Puffer halten), eine elegante Methode für das mehrzeilige Greppen zu finden.

Nehmen wir zum Beispiel eine Textdatei mit Hostnamen und einigen Informationen zu jedem Host, mit viel Müll dazwischen, den ich nicht interessiere.

Host: foo1
some junk, doesnt matter
some junk, doesnt matter
Info: about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Info: a second line about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Host: foo2
some junk, doesnt matter
Info: about foo2 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter

Für mich würde ein awk-Skript, um nur die Zeilen mit dem Hostnamen und der entsprechenden infoZeile abzurufen, etwas mehr dauern, als ich mit sed tun kann:

sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt

Ausgabe sieht aus wie:

Host: foo1
Info: about foo1 that I really care about!!
Host: foo1
Info: a second line about foo1 that I really care about!!
Host: foo2
Info: about foo2 that I really care about!!

(Beachten Sie, dass dies Host: foo1in der Ausgabe zweimal vorkommt.)

Erläuterung:

  1. -n Deaktiviert die Ausgabe, sofern nicht ausdrücklich gedruckt
  2. erste Übereinstimmung, findet und legt die Host:Zeile in den Haltepuffer (h)
  3. Bei der zweiten Übereinstimmung wird die nächste Info: -Zeile gefunden, aber zuerst wird die aktuelle Zeile (x) im Musterpuffer mit dem Haltepuffer Host:ausgetauscht und die Zeile gedruckt (p) . Anschließend wird die Info: -Zeile erneut ausgetauscht (x) und gedruckt (p).

Ja, dies ist ein vereinfachtes Beispiel, aber ich vermute, dass dies ein häufiges Problem ist, das von einem einfachen Einzeiler schnell behoben wurde. Für viel komplexere Aufgaben, bei denen Sie sich nicht auf eine bestimmte, vorhersehbare Reihenfolge verlassen können, ist awk möglicherweise besser geeignet.

Jens Jensen
quelle
2
In diesem Fall könnten Sie aber einfach grep verwenden:grep 'Host\|Info'
Pithikos
Wenn nach einem bestimmten Host zwei Infozeilen stehen, möchte @JensJenson, dass beiden Infozeilen eine Infozeile vorangestellt wird. Ich denke, ich werde die Antwort entsprechend bearbeiten. Pithikos, grep wird dann nicht ausreichen.
Aaron McDaid
3
@JensJenson, das awkÄquivalent Ihres sed-Codes ist auch ziemlich kurz:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Aaron McDaid
11

Obwohl die Antwort von @ January und das Beispiel nett sind, hat mir die Erklärung nicht gereicht. Ich musste viel suchen und lernen, bis ich verstanden hatte, wie genau das sed -n '1!G;h;$p'funktioniert. Deshalb möchte ich den Befehl für jemanden wie mich näher erläutern.

Lassen Sie uns zunächst sehen, was der Befehl bewirkt.

$ echo {a..d} | tr ' ' '\n' # Prints from 'a' to 'd' in each line
a
b
c
d
$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;$p'
d
c
b
a

Es kehrt die Eingabe um, wie es der tacBefehl tut.

sedLiest Zeile für Zeile, also lasst uns sehen, was auf dem Patten- und dem Haltebereich in jeder Zeile passiert . Wenn der hBefehl den Inhalt des Musterbereichs in den Haltebereich kopiert, haben beide Bereiche denselben Text.

Read line    Pattern Space / Hold Space    Command executed
-----------------------------------------------------------
a            a$                            h
b            b\na$                         1!G;h
c            c\nb\na$                      1!G;h
d            d\nc\nb\na$                   1!G;h;$p

In der letzten Zeile $pdruckt d\nc\nb\na$, die formatiert

d
c
b
a

Wenn Sie den Musterbereich für jede Zeile anzeigen möchten, können Sie einen lBefehl hinzufügen .

$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;l;$p'
a$
b\na$
c\nb\na$
d\nc\nb\na$
d
c
b
a

Ich fand es sehr hilfreich, dieses Video-Tutorial zu sehen. Verstehen, wie sed funktioniert , da der Typ zeigt, wie jeder Raum Schritt für Schritt verwendet wird. Der Abstand zum Halten wird im 4. Tutorial beschrieben. Ich empfehle jedoch, alle Videos anzusehen, wenn Sie nicht mit diesen vertraut sind sed.

Auch das Sed-Dokument von GNU und das Sed-Tutorial von Bruce Barnett sind sehr gute Referenzen.

Sanghyun Lee
quelle
2
Ich denke, es wird auch hilfreich sein zu erwähnen, dass der Speicherplatz für alle praktischen Zwecke leer ist, es sei denn, wir fügen etwas hinzu.
Naveed