Kapitel 5. spaceballs

Ergänzungen zum pattern space

sed kennt noch weitere Kommandos zur Manipulation des pattern space. Das Kommando 'D' startet einen neuen Zyklus - wie das Kommando 'd', wenn der Inhalt des pattern space kein Newline enthält. Falls der pattern space allerdings ein Newline enthält, wird der Inhalt des pattern space bis einschließlich zum ersten Newline gelöscht, und danach ein neuer Zyklus mit dem verbleibenden pattern space gestartet, ohne eine weitere Zeile von der Eingabe zu lesen. Das Kommando 'N' hängt ein Newline an den Inhalt des pattern space, liest eine neue Zeile ein, welche nach dem Newline eingefügt wird. Kann keine neue Zeile mehr eingelesen werden (Dateiende), dann wird das Programm an dieser Stelle abgebrochen. Das Kommando 'P' gibt den Inhalt des pattern space bis zum ersten Newline aus.

Das folgende Beispiel löscht alle konsekutiven Leerzeilen in einer Datei. Ist am Dateianfang eine Leerzeile, so bleibt sie erhalten; am Dateiende werden alle Leerzeilen gelöscht.

sed -e '/^$/N;/\n$/D'

Dieses Beispielprogramm arbeitet wie folgt: Ist die erste Zeile der Eingabe leer, trifft die erste Adresse des Programms zu und das Kommando 'N' fügt in den pattern space ein Newline gefolgt von der zweiten Zeile der Eingabe ein. Ist auch diese zweite Zeile der Eingabe leer, befindet sich im pattern space nur das Newline, sonst nichts. In diesem Fall trifft anschließend die zweite Adresse zu, sodass das Kommando 'D' das Newline aus dem pattern space löscht und einen neuen Zyklus mit dem bestehenden (leeren) pattern space startet, also ohne zu Beginn die nächste Zeile von der Eingabe einzulesen. Nun trifft die erste Adresse wieder zu, woraufhin wieder ein Newline gefolgt von der nächsten Eingabezeile in den pattern space geladen wird. Sobald eine Eingabezeile nicht leer ist, wird sie also hinter dem Newline eingefügt. Wenn dies passiert, trifft die zweite Adresse allerdings nicht mehr zu. In diesem Fall wird schließlich ein einziges Newline gefolgt von der nicht-leeren Eingabezeile ausgegeben. Danach wird ein neuer Zyklus gestartet. Ist zu Beginn eines Zyklus die Eingabezeile nicht leer, trifft weder die erste noch die zweite Adresse zu und der pattern space wird unverändert ausgegeben. Danach startet wieder der nächste Zyklus. Somit werden alle konsekutiven Leerzeilen vor einer nicht-leeren Zeile zu einer einzigen Leerzeile reduziert, während alle nicht-leeren Zeilen unverändert erhalten bleiben.

Einmal hold space und zurück

Neben dem pattern space, in den die Zeile geladen und dort manipuliert wird, kennt sed noch den hold space, der zu Programmbeginn leer ist, aber durch verschiedene Befehle manipuliert werden kann. Der hold space wird hauptsächlich dann verwendet wenn man das Operationsfeld eines einzigen Kommandos auf mehrere Zeilen ausdehnen will oder sich Zeilen für später aufheben muss.

Das Kommando 'h' überschreibt den hold space mit dem Inhalt des pattern space; die umgekehrte Operation wird durch das Kommando 'g' erreicht. Es gibt auch groß geschriebene Versionen dieser Kommandos, welche den Zielspace nicht überschreiben, sondern daran ein Newline gefolgt vom Inhalt des Quellspace anhängen.

Zur Verinnerlichung des Konzepts des hold space ein sehr einfaches Beispiel, in dem die erste Zeile zurückbehalten wird und erst nach der letzten Zeile geschrieben wird. Das Programm kopiert also die erste Zeile in den hold space, gibt alle anderen aus, und nach Erreichen des Dateiendes wird der Inhalt des hold space in den pattern space kopiert, der dann noch ausgegeben werden muss. Das und nichts anderes tut der folgende Einzeiler.

sed -n -e '1h;1!p;${g;p;}'

Das folgende Beispiel gibt alle Zeilen sofort aus, die nicht in einem '/begin/,/end/'-Block liegen, den Rest erst bei Dateiende. Im Hinblick auf ein sed-Programm heißt das, Zeilen im Block '/begin/,/end/' werden an den hold space angehängt. Zu beachten ist nur, dass der Befehl 'H' dem Inhalt des hold space zuerst ein Newline und dann der pattern space anhängt. Deshalb muss man bei der Ausgabe das erste Zeichen (sicher ein Newline) unterdrücken.

sed -n -e '/begin/,/end/H;/begin/,/end/!p;${g;s/^.//;p;}'

Anzumerken ist hierbei noch, dass sed den Inhalt des pattern space als eine Zeile ansieht, egal ob da noch ein oder mehrere Newline enthalten sind. Aus diesem Grund unterdrückt das Kommando 's/^.//' nicht alle Buchstaben nach einem Newline, sondern wirklich nur das erste Zeichen im hold space.

Das Kommando 'G' hat folgenden Effekt: es wird an den pattern space ein Newline und anschließend der Inhalt des hold space angehängt. Das kann man für die verschiedensten Zwecke ausnützen. Das Script

sed -e 'G'

fügt nach jeder Zeile eine Leerzeile ein (der hold space ist ja leer). Mit sed kann man auch die Funktionsweise von tac (ein umgekehrtes cat; dreht die Reihenfolge der Zeilen um) nachbilden:

sed -n -e 'G;h;$p'

mit dem kleinen Schönheitsfehler, dass am Ende eine Leerzeile zu viel ausgegeben wird - sie ist die Leerzeile, die in der ersten Zeile dem pattern space unnötigerweise angehängt wurde. Diesen Fehler beheben gleich beide folgenden Programme.

sed -n -e 'G;h;$s/.$//p'
sed -n -e '1!G;h;$p'

Mit dem Kommando 'x' werden die Inhalte der beiden spaces ausgetauscht. Abschließend zu diesem Kapitel möchte ich ein längeres Beispiel (Danke an Ulf Bro) vorstellen, das umgebrochene Absätze in eine einzelne Zeile umwandelt:

# Zeilen, die nicht leer sind werden dem Hold-Raum angehängt
# Bei Leerzeilen wird der Inhalt des Hold-Raums in den
# Pattern-Raum verlagert. Der Hold-Raum wird entleert
# Erste Newline wird entfernt, die anderen in Leerzeichen
# umgewandelt
/^$/! H
/^$/ {
    x
    s/\n//
    s/\n/ /g
    p
}
# Letzte Zeile nicht vergessen
$ {
    g
    s/\n//
    s/\n/ /g
    p
}