Wie setzt der Befehl '1! G; h; $! D' den Inhalt einer Datei um?

20

Meine Frage bezieht sich auf die sedspezifische Lösung, die in dieser Antwort für diese Frage des umgekehrten Greppings angegeben ist . Die sed/ grep-Lösung, die ich nicht entziffern kann, ist die folgende:

sed '1!G;h;$!d' file

Kann jemand bitte diesen Befehl entschlüsseln?

Ich weiß aus VI (M) -Wissen, dass G die letzte Zeile der Datei bezeichnet und dass in sed ein Knall (!) Gefolgt von einer Adressarbeit ein bisschen bedeutet grep -v, dass es nicht zu dieser Zeile passt. Aber als Ganzes ist das Inline-Sed-Skript über mir.

Aussenseiter
quelle
3
... sehr langsam ...
mikeserv
1
Da deutet mikeserv an. Man sollte beachten, dass dieses knifflige sedRezept eine äußerst ineffiziente Methode (O (n ^ 2/2) -Komplexität) ist, um Zeilen in einer Datei umzukehren. Es wäre unerschwinglich langsam für Dateien mit einer großen Anzahl von Zeilen. Eine viel effizientere Alternative zur Umkehrung der Zeilenreihenfolge finden Sie tacin GNU coreutils.
Arielf

Antworten:

35

Dies kehrt die Datei zeilenweise um.

sed '1! G; h; $! d' Datei

Erstens sedhat ein Hold-Space und ein Pattern-Space . Wir müssen sie unterscheiden, bevor wir uns auf diesen speziellen Befehl konzentrieren.

Wenn sedeine neue Zeile gelesen wird, wird sie in den Musterraum geladen. Daher wird dieser Speicherplatz jedes Mal überschrieben, wenn eine neue Zeile verarbeitet wird. Auf der anderen Seite ist der Speicherplatz über die gesamte Verarbeitung konsistent und die Werte können dort zur späteren Verwendung gespeichert werden.


Zum Befehl:

Es gibt 3 Befehle in dieser Aussage: 1!G, hund$!d

  • 1!Gbedeutet, dass der GBefehl in jeder Zeile außer der ersten ausgeführt wird (die !negiert die 1). GMittel zum anhängen , was im Laderaum in den Musterraum ist.

  • hgilt für jede Zeile. Es kopiert den Pattern-Space in den Hold-Space (und überschreibt ihn).

  • $!dgilt für jede Zeile mit Ausnahme der letzten ( $stellt die letzte Zeile dar, !negiert sie). dist der Befehl zum Löschen der Zeile (Musterabstand).


  1. Wenn nun die erste Zeile gelesen wird, sedwird der hBefehl ausgeführt. Die erste Zeile wird in den Laderaum kopiert. Dann wird es gelöscht, da es der $!Bedingung entspricht. sedfährt mit der zweiten Zeile fort.
  2. Die zweite Zeile entspricht der Bedingung 1!(es ist nicht die erste Zeile), und daher wird der Haltebereich (der die erste Zeile enthält) an den Musterbereich (der die zweite Zeile enthält) angehängt. Danach folgt im Musterraum die zweite Zeile, gefolgt von der ersten Zeile, die durch eine neue Zeile begrenzt wird. Jetzt gilt der hBefehl (wie in jeder Zeile); Alles, was sich im Pattern-Space befindet, wird in den Hold-Space kopiert. Die dritte Anweisung ( $!d) gilt: Die Zeile wird aus dem Musterbereich gelöscht.
  3. Schritt 2 ist nun mit allen Zeilen abgeschlossen. Wir springen zur letzten Zeile.
  4. In der letzten Zeile ( $) ist fast alles von Schritt 2 erledigt, nicht jedoch der Löschteil ( d). sedWenn diese Option ohne aufgerufen wird -n, wird der Musterbereich am Ende der Verarbeitung für jede Eingabezeile automatisch gedruckt. Wenn dies nicht gelöscht wird, wird der Musterbereich gedruckt. Es enthält nun alle Zeilen in umgekehrter Reihenfolge .
Chaos
quelle
1
@Geek Nein, der hBefehl kopiert den Musterbereich in den Haltebereich, der bis zum sedEnde bestehen bleibt . Nach dem Ende des Skripts wird alles gelöscht, da die Binärdatei beendet wird.
Chaos
2
Können wir uns den Laderaum als Register für Vim vorstellen? Sind sie auch nummeriert? Oder gibt es nur einen von ihnen?
Geek
2
@Geek In sedgibt es nur einen Laderaum. Es ist wie eine Variable, die etwas enthalten kann.
Chaos
1
@ user1717828 Wäre dies nicht der Fall, würde bei der Verarbeitung die erste Zeile gedruckt. Da sed nicht mit aufgerufen wird, müssen -nwir jede Zeile mit Ausnahme der letzten löschen. In der letzten Zeile hängt sed alles vom Hold-Raum bis zum Pattern-Raum an. Und weil der dBefehl nicht ausgeführt wird, wird die Zeile gedruckt (diese Zeile enthält jetzt die gesamte Datei in umgekehrter Reihenfolge).
Chaos
1
(1) Die Unterfrage / Beobachtung des OP (im Kommentar) lautet: „Der hBefehl in der letzten Zeile ist also eine Art No-Op.“ (Mit zusätzlicher Betonung und leicht umschrieben). Er hat recht; Wenn seddie letzte Eingabezeile verarbeitet wird , Gliest der den Haltebereich (um ihn an den Musterbereich anzuhängen) und hkopiert dann den Musterbereich in den Haltebereich, auf den nie wieder verwiesen wird . Wir könnten genauso gut sagen sed 'G;$!h;$!d'oder sed 'G;$!{h;d}'. (2) Wir könnten vermeiden, dindem wir sagen sed -n 'G;h;$p'.
Scott