Der Texteditor Stream Editor, kurz sed, ist omnipräsent – allerdings fast immer beschränkt auf das Substitute-Kommando. Das verwundert nicht, immerhin beschreiben die Autoren diese Funktion doch selbst Schweizer Taschenmesser. Aber sed ist so viel mehr.
Mit dem Stream Editor lässt sich deutlich mehr Struktur in Dokumente und dergleichen bringen.
Sed-Kommandos sind nicht hübsch anzusehen, selbst wenn auf komplexe reguläre Ausdrücke verzichtet wird – Beispiel gefällig?
sed -n -e '/X/ { H ; d } ; /Zeile/ { p ; d } ; G ; p' test
Kryptisch, ja – aber auch nützlich! Mehr dazu später, aber so viel: Hier geht es um eine sortierte Ausgabe von Zeilen. Wer die sed-Anweisung aus dem Effeff versteht, kann sich an dieser Stelle direkt auf vordergründig weniger kryptische Anweisungen wie „You lying stupid idiot! stürzen.
Im Grunde ist sed gar nicht sonderlich kompliziert, zumindest, wenn man bei den Basics und dem Suchen-und-Ersetzen-Befehl (s für Substitut) bleibt: sed arbeitet Zeile für Zeile eines Dokuments ab, sucht in jeder Zeile nach einem gegebenen Muster und ersetzt dieses durch einen gegebenen Text – was zum altbekannten Kommando führt:
sed s/MUSTER/KOMMANDO sed s/foo/bar
Und möchte man nicht „foo“ durch „bar“ ersetzen, sondern zu „foobar“ ergänzen, kommen Dinge aus der Welt der regulären Ausdrücke hinzu:
sed s/(foo)/\1bar
Aufgrund der Klammern wird „foo“ als Matchgruppe betrachtet und in der Variablen „1“ gespeichert, die im Kommando-Teil mit „\1“ wieder genutzt werden kann. Solche Fälle machen vermutlich 70 Prozent aller sed-Vorkommen im realen Leben aus – wobei die Komplexität der regulären Ausdrücke kaum Grenzen kennt.
Von der Syntax mal abgesehen, ist die Arbeitsweise (zumindest im Abstrakten) ziemlich einfach zu verstehen – Suchen-und-Ersetzen-Funktionen gibt es schließlich sogar in Word & Co. Über die interne Funktionsweise von sed sagt das aber wenig aus; eine simple Sortierung hilft da schon eher.
Sortieren mit sed
Um sed produktiv einsetzen zu können, muss man die Logik dahinter verstehen. Sed arbeitet zeilenweise, in Zyklen und mit zwei Puffern. Zunächst liest sed eine Zeile ein (Input), speichert den deren Inhalt im so genannten Pattern Space (Puffer 1) und führt dann die gegebenen Kommandos auf diesen Pattern Space aus. Damit ist ein Zyklus beendet und mit der nächsten Zeile beginnt das Prozedere von vorn.
Standardmäßig gibt sed jeden Pattern Space (also jede Zeile) aus, egal, ob in dieser das gewünschte Muster vorkommt oder nicht. Daher starten sehr viele sed-Kommandos mit dem Kommandozeilenparameter „-n“, der dieses Verhalten unterdrückt. Gewünschte Ausgaben müssen dann explizit mit dem Kommando „p“ (print) provoziert werden.
Möchten Sie als zum Beispiel nur und alle Zeilen eines Dokuments „test“ ausgeben, die mit „Zeile“ beginnen, sieht sed auf einmal ziemlich freundlich aus:
sed -n '/Zeile/ p' test
Wie beim bekannten s-Kommando leitet der Slash die Suche nach dem Muster (hier „Zeile“) ein und nach dem zweiten Slash folgt das Kommando, hier schlichtes Drucken (p für print).
Zeilen mit gefundenem Muster können auch mehrere Anweisungen bekommen: Angenommen, es sollen die Zeilen mit dem Wort „Zeile“ plus die jeweils nächste Zeile gedruckt werden. Dann muss dem Pattern Space vor dem Druck noch diese nächste Zeile übergeben werden:
sed -n '/Zeile/ { N ; p }' test
Mehrere Kommandos stehen schlicht in geschweiften Klammern und werden durch Semikola getrennt. Neu ist hier nur das „N“. Nach der Suche nach „Zeile“ entspricht der Pattern Space dieser einen Zeile. Das Kommando N fügt diesem Pattern Space dann ein Newline (\n) hinzu und anschließend die nächste Zeile (Input) der übergebenen Datei. Über p wird dann der ganze Pattern Space ausgedruckt, in der Art:
Fällt Ihnen etwas auf? Sed arbeitet normalerweise auf Zeilenebene – nun stehen im Pattern Space für den aktuellen Zyklus aber plötzlich zwei Zeilen zur Verfügung. Das heißt auch, dass sich über solch ein Konstrukt Muster finden lassen, die sich über zwei oder mehr Zeilen erstrecken.
Sollen solche „Doppel-Zeilen“ etwa nur ausgegeben werden, wenn in der zweiten Zeile „irgendetwas“ steht, ließe sich die Bedingung ganz simpel realisieren:
sed -n '/Zeile/ { N ; /irgendetwas/ p }' test
Hier ist lediglich „/irgendetwas/“ als Filter/Suche/Bedingung hinzugekommen: Wird „irgendetwas“ im neuen Zweizeiligen Pattern Space gefunden, wird er ausgedruckt, falls nicht, endet der Zyklus. Die Ausgabe wäre folglich:
Stand: 08.12.2025
Es ist für uns eine Selbstverständlichkeit, dass wir verantwortungsvoll mit Ihren personenbezogenen Daten umgehen. Sofern wir personenbezogene Daten von Ihnen erheben, verarbeiten wir diese unter Beachtung der geltenden Datenschutzvorschriften. Detaillierte Informationen finden Sie in unserer Datenschutzerklärung.
Einwilligung in die Verwendung von Daten zu Werbezwecken
Ich bin damit einverstanden, dass die Vogel IT-Medien GmbH, Max-Josef-Metzger-Straße 21, 86157 Augsburg, einschließlich aller mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen (im weiteren: Vogel Communications Group) meine E-Mail-Adresse für die Zusendung von Newslettern und Werbung nutzt. Auflistungen der jeweils zugehörigen Unternehmen können hier abgerufen werden.
Der Newsletterinhalt erstreckt sich dabei auf Produkte und Dienstleistungen aller zuvor genannten Unternehmen, darunter beispielsweise Fachzeitschriften und Fachbücher, Veranstaltungen und Messen sowie veranstaltungsbezogene Produkte und Dienstleistungen, Print- und Digital-Mediaangebote und Services wie weitere (redaktionelle) Newsletter, Gewinnspiele, Lead-Kampagnen, Marktforschung im Online- und Offline-Bereich, fachspezifische Webportale und E-Learning-Angebote. Wenn auch meine persönliche Telefonnummer erhoben wurde, darf diese für die Unterbreitung von Angeboten der vorgenannten Produkte und Dienstleistungen der vorgenannten Unternehmen und Marktforschung genutzt werden.
Meine Einwilligung umfasst zudem die Verarbeitung meiner E-Mail-Adresse und Telefonnummer für den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern wie z.B. LinkedIN, Google und Meta. Hierfür darf die Vogel Communications Group die genannten Daten gehasht an Werbepartner übermitteln, die diese Daten dann nutzen, um feststellen zu können, ob ich ebenfalls Mitglied auf den besagten Werbepartnerportalen bin. Die Vogel Communications Group nutzt diese Funktion zu Zwecken des Retargeting (Upselling, Crossselling und Kundenbindung), der Generierung von sog. Lookalike Audiences zur Neukundengewinnung und als Ausschlussgrundlage für laufende Werbekampagnen. Weitere Informationen kann ich dem Abschnitt „Datenabgleich zu Marketingzwecken“ in der Datenschutzerklärung entnehmen.
Falls ich im Internet auf Portalen der Vogel Communications Group einschließlich deren mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen geschützte Inhalte abrufe, muss ich mich mit weiteren Daten für den Zugang zu diesen Inhalten registrieren. Im Gegenzug für diesen gebührenlosen Zugang zu redaktionellen Inhalten dürfen meine Daten im Sinne dieser Einwilligung für die hier genannten Zwecke verwendet werden. Dies gilt nicht für den Datenabgleich zu Marketingzwecken.
Recht auf Widerruf
Mir ist bewusst, dass ich diese Einwilligung jederzeit für die Zukunft widerrufen kann. Durch meinen Widerruf wird die Rechtmäßigkeit der aufgrund meiner Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt. Um meinen Widerruf zu erklären, kann ich als eine Möglichkeit das unter https://contact.vogel.de abrufbare Kontaktformular nutzen. Sofern ich einzelne von mir abonnierte Newsletter nicht mehr erhalten möchte, kann ich darüber hinaus auch den am Ende eines Newsletters eingebundenen Abmeldelink anklicken. Weitere Informationen zu meinem Widerrufsrecht und dessen Ausübung sowie zu den Folgen meines Widerrufs finde ich in der Datenschutzerklärung.
Zeile 1 irgendetwas
Man kann sed-Anweisungen also im Grunde oft als if-Abfragen begreifen:
if foobar; then echo wahr; fi sed -n '/foobar/ a wahr'
Das Kommando „a“ fügt schlicht einen beliebigen Text an (append) – beziehungsweise gibt nur diesen Text aus, da weitere Ausgaben über „-n“ unterbunden sind.
Der Pattern Space lässt sich so also erweitern, als Ganzes manipulieren und dann ausgeben – jeweils bezogen auf einen Zyklus. Fehlt noch der zweite Puffer, der Hold Space, der Daten über Zyklen hinweg speichern kann.
Der Hold Space
Der zweite Puffer namens Hold Space speichert Daten nicht für einen Zyklus, sondern die ganze Abarbeitung des sed-Statements. Und natürlich können Daten vom Pattern Space in den Hold Space und umgekehrt kopiert werden.
Auch hier leistet das obige Beispiel gute Dienste. Wegen der Komplexität hier aber zunächst der Inhalt einer Datei „test“:
Zeile 1: foobar Zeile 2: hallo welt Zeile 3: foo Zeile 4: bar X1 Zeile 5: ende X2 [Anmerkung: Diese leere letzte Zeile gehört zum Inhalt.]
Die Aufgabe lautet nun: Erst alle Zeilen ausgeben, die „Zeile“ enthalten, dann die X-Zeilen. Konzeptionell klingt das recht simpel: Zeilen mit „Zeile“ werden wie gewohnt ausgegeben, X-Zeilen landen im Hold Space und werden am Ende gesammelt in den Pattern Space kopiert, der dann wiederum ausgegeben werden kann (ein Kommando „Drucke Hold Space“ gibt es nicht).
Praktisch bedeutet das drei neue Kommandos, nämlich „d“ (delete), „H“ (Pattern Space an Hold Space anfügen) und „G“ (Hold Space an Pattern Space anfügen). Etwas genauer: H und G fügen zunächst ein Newline und dann den jeweiligen Puffer-Inhalt hinzu.
sed -n '/X/ { H ; d } ; /Zeile/ { p ; d } ; G ; p' test
Zunächst prüft das sed-Kommando hier also für jede Zeile, ob im Input ein „X“ vorkommt. Falls ja, wird diese Zeile zunächst dem Hold Space hinzugefügt (H) und dann aus dem Pattern Space gelöscht (d). Dann wird geprüft, ob im Input „Zeile“ vorkommt. Falls ja, wird der Pattern Space ausgedruckt und anschließend ebenfalls gelöscht.
Sobald eine Zeile keine der beiden Bedingungen erfüllt, hier also die letzte leere Zeile, werden die Inhalte aus dem Hold Space (also alle X-Zeilen) in den Pattern Space kopiert (G), welcher dann gedruckt wird (p).
Das Ergebnis:
Zeile 1: foobar Zeile 2: hallo welt Zeile 3: foo Zeile 4: bar Zeile 5: endeX1 X2
Das ist nun ein sehr einfaches (fragiles und konstruiertes) Beispiel, um die Funktionsweise der beiden Puffer und der zyklischen Verarbeitung zu veranschaulichen (ein Schelm wer meint, die sed-Macher selbst hätten Verständnisprobleme, nur weil das Puffer-Kapitel in der offiziellen Dokumentation lediglich ein „TODO“ zeigt – seit nunmehr 47 Jahren.
In der Praxis würde solch eine Sortierung mit sed mindestens noch allerlei reguläre Ausdrücke verschlingen, bis hin zur völligen Unlesbarkeit. Und vielleicht würde man manche Dinge ganz anders lösen. Aber mit dem Verständnis der Puffer und der sed-Kommando-Syntax hat man so zumindest zwei sauber trennbare Baustellen, schließlich sind reguläre Ausdrücke alles andere als sed-spezifisch.
Sed hat mittlerweile auch eine eingebaute Debug-Funktion, die sich einfach per „--debug“-Schalter aktivieren lässt. Die Ausgabe sieht dann so aus, hier ein kompletter Zyklus für eine der X-Zeilen aus dem obigen Beispiel:
INPUT: 'test' line 7 PATTERN: X2 COMMAND: /X/ { COMMAND: H HOLD: \nX1\nX2 COMMAND: d END-OF-CYCLE:
Input ist hier also die 7. Zeile, im Pattern Space liegt „X2“, das Kommando „/X/“ sucht nach einem X im Pattern Space, da dieses gefunden wurde wird „H“ ausgeführt, was zu einem Hold Space von „\nX1\nX2“ führt – bevor zum Schluss mittels „d“ der Pattern Space gelöscht wird. Mit der Debug-Funktion lässt sich wunderbar arbeiten, sofern die grundlegende sed-Arbeitsweise verstanden ist.
Ein kleiner Bonus
Hier noch ein kleiner Ausflug auf einen Nebenschauplatz, einfach, weil es den praktischen Einstieg ungemein erleichtert: Es ist gute Sitte, dass Dateien mit einer leeren Zeile enden. Sich auf solche zu verlassen ist vielleicht nicht immer die beste Idee; wenn man aber zum Beispiel sicher ist, dass im obigen Beispiel nur Zeilen mit „Zeile“ oder „X“ vorkommen, kann man die letzte leere Zeile durchaus verwenden, um ein letztes Kommando auszuführen, wie eben etwa das finale Drucken des Pattern Spaces. Also gilt es zu prüfen, ob eine leere Zeile am Ende steht und falls nicht, muss sie hinzugefügt werden:
if [ $(tail -c1 test; echo x) != $'\nx' ]; then echo "" >> test; fi
Die eckigen Klammern stehen für das test-Kommando – und wenn der Ausdruck wahr ist, fügt echo eine Leerzeile an die Datei „test“ an. Innerhalb der Klammern wird über tail das letzte Zeichen (-c1) der Datei „test“ plus „x“ ausgegeben. Dann wird geprüft, ob dieser Ausdruck nicht gleich „\nx“ (also Newline plus x) ist.
Man möchte meinen, sed würde einen eleganteren Weg dafür bereitstellen, tut es aber nicht. Wenn Sie Dateien nicht verändern wollen, können Sie dieses Hilfsmittel natürlich auch für eine Pipe nutzen, wobei cat einfach den Inhalt der Datei und das echo-Kommando konkateniert:
if [ $(tail -c1 test; echo x) != $'\nx' ]; then echo "" ; fi | cat test - | sed-KOMMANDO