spaceballs

Ergänzungen zum pattern space

sed kennt noch weitere Kommandos zur Manipulation des pattern space. Das Kommando 'D' löscht den Inhalt des pattern space bist zum ersten newline. Ist darin anschließend noch Text enthalten, wird ein neuer Zyklus gestartet, ohne eine neue Input-Zeile einzulesen. Ist der pattern space degegen leer, beginnt ein normaler Zyklus. 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 Beispiel dazu 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'

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 ein Leerzeichen 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
}