sed ist der Stream Editor der Unix-Welt: ein zeilenorientiertes Werkzeug, das Eingabe Zeile für Zeile durch ein kleines Skript schickt und das Ergebnis nach stdout ausgibt. Klassisch für Substitutionen gebaut, beherrscht sed auch Löschen, Einfügen, Filtern und Umsortieren. Wer ihn beherrscht, schreibt in einer einzigen Zeile, wofür andere ein Python-Skript brauchen.

Was sed macht

sed liest seine Eingabe Zeile für Zeile und wendet auf jede Zeile ein Skript an — eine Folge von Kommandos, die aus der Zeile etwas Neues machen. Das Ergebnis landet auf stdout, die Quelldatei bleibt unverändert (es sei denn, du nutzt -i). Konzeptuell ist sed ein Filter wie grep oder awk, nur eben mit Editier-Fähigkeit.

Die häufigste Aufgabe ist die Substitution: ein Muster suchen und durch etwas anderes ersetzen. Daneben kann sed Zeilen löschen, vor oder nach einer Trefferzeile etwas einfügen, Bereiche extrahieren und mit Hold-Buffern sogar mehrzeilige Operationen ausführen. Letzteres ist allerdings selten der Punkt, an dem sed noch lesbar bleibt — für komplexe Logik greift man besser zu awk oder Python.

Bash Klassische Substitution
sed 's/alt/neu/g' datei.txt

Substitution s/PATTERN/REPL/FLAGS

Das s-Kommando ist der absolute Star — gut 90 Prozent aller sed-Aufrufe in freier Wildbahn sind reine Substitutionen. Die Form ist s/PATTERN/REPL/FLAGS: such PATTERN, ersetz durch REPL, modifiziert durch FLAGS.

FlagWirkung
gGlobal — alle Treffer pro Zeile, nicht nur den ersten
i / ICase-insensitive (GNU-Erweiterung)
1-NNur das N-te Match in der Zeile ersetzen
pGeänderte Zeile zusätzlich ausgeben (sinnvoll mit -n)
w DATEIGeänderte Zeile zusätzlich in DATEI schreiben
eErgebnis als Shell-Kommando ausführen (gefährlich!)

Ohne g ersetzt sed nur den ersten Treffer pro Zeile — eine häufige Stolperfalle. s/foo/bar/ ändert in foo foo foo nur das erste foo; s/foo/bar/g ändert alle drei. 2 ersetzt nur das zweite Vorkommen, 3g das dritte und alle weiteren.

Bash Nur erstes Match in jeder Zeile
sed 's/foo/bar/' datei.txt
Bash Alle Treffer, case-insensitive
sed 's/foo/bar/gi' datei.txt

Delimiter wechseln

Der Trenner zwischen PATTERN, REPL und FLAGS muss kein Slash sein — sed akzeptiert jedes Zeichen direkt nach dem s als Delimiter. Das ist Gold wert, sobald das Pattern selbst Slashes enthält, etwa bei Pfaden oder URLs.

Bash Pipe statt Slash bei Pfad-Substitution
sed 's|/usr/local|/opt|g' config.txt

Mit Slash als Delimiter müsste man jeden / im Pattern escapen: s/\/usr\/local/\/opt/g — unleserlich. Mit |, ,, # oder @ als Trenner fällt der Backslash-Salat weg. Konvention: bei Pfaden gerne |, bei URLs # oder @. Wichtig: das gewählte Zeichen darf weder im Pattern noch im Replacement vorkommen, sonst wird der Ausdruck falsch zerlegt.

Bash Komma als Trenner bei URLs
sed 's,https://alt.example.com,https://neu.example.com,g' index.html

Inline-Edit mit -i

Standardmäßig schreibt sed nach stdout und lässt die Quelldatei in Ruhe. Mit -i ändert sed die Datei direkt. Praktisch für Massenänderungen, gefährlich ohne Backup.

Bash Datei direkt ändern, mit Backup
sed -i.bak 's/alt/neu/g' config.conf

-i.bak schreibt die Änderung in config.conf und legt das Original als config.conf.bak daneben. Ohne Suffix (-i '' auf BSD, -i ohne Argument auf GNU) wird ohne Backup überschrieben.

Bash GNU-Linux: in-place ohne Backup
sed -i 's/alt/neu/g' datei.txt
Bash BSD/macOS: in-place ohne Backup
sed -i '' 's/alt/neu/g' datei.txt

Adressen — Zeilen wählen

Standardmäßig wirken sed-Kommandos auf jede Eingabezeile. Mit Adressen schränkt man den Wirkungsbereich ein. Adressen stehen vor dem Kommando und sind entweder Zeilennummern, Patterns oder Bereiche.

AdresseBedeutung
5pNur Zeile 5
$dNur die letzte Zeile
5,10dZeilen 5 bis 10
/PATTERN/dAlle Zeilen, die PATTERN enthalten
/START/,/END/pBereich von erstem START-Match bis erstem END-Match
2,$dAb Zeile 2 bis Ende
1~2Jede zweite Zeile ab Zeile 1 (GNU)
5!dNegation — alle außer Zeile 5

Wichtig: ohne -n gibt sed jede Eingabezeile automatisch aus, und p druckt zusätzlich. Das führt zu Doppelausgaben. Wer „nur Zeile 5 anzeigen” will, kombiniert -n mit p: sed -n '5p' datei.

Bash Nur Zeile 5 anzeigen
sed -n '5p' datei.txt
Bash Alle Kommentarzeilen löschen
sed '/^#/d' config.conf
Bash Bereich zwischen BEGIN und END drucken
sed -n '/^BEGIN/,/^END/p' log.txt

Wichtige Befehle

Das s-Kommando ist nur eines von vielen. sed kennt eine ganze Reihe von Ein-Buchstaben-Kommandos, die jeweils auf der aktuellen Zeile (dem Pattern Space) operieren.

BefehlWirkung
s/X/Y/FSubstitution
dPattern Space löschen, nächste Zeile lesen
pPattern Space ausgeben (typisch mit -n)
nNächste Zeile lesen, Pattern Space überschreiben
NNächste Zeile anhängen (mit Newline dazwischen)
i\TEXTTEXT vor der Zeile einfügen
a\TEXTTEXT nach der Zeile anhängen
c\TEXTZeile (oder Bereich) durch TEXT ersetzen
=Zeilennummer ausgeben
qSofort beenden
y/abc/xyz/Translation (zeichenweise, wie tr)
h / HPattern Space in Hold Space kopieren / anhängen
g / GHold Space in Pattern Space kopieren / anhängen
xPattern und Hold Space tauschen

Mehrere Kommandos werden mit ; oder per -e-Flag verkettet. sed -e 'cmd1' -e 'cmd2' ist gleichwertig zu sed 'cmd1;cmd2'. Bei längeren Skripten lohnt eine separate Datei mit -f skript.sed.

Bash Mehrere Substitutionen verketten
sed 's/foo/FOO/g; s/bar/BAR/g' datei.txt
Bash Vor jeder Match-Zeile etwas einfügen
sed '/ERROR/i\--- Fehler folgt ---' log.txt

Backreferences

Im Replacement-Teil einer Substitution stehen \1, \2, …, \9 für die in () eingefangenen Capture-Groups des Patterns. & ist dabei eine Sonderform und steht für den gesamten Match.

Bash Vor- und Nachname tauschen
sed -E 's/(\w+) (\w+)/\2 \1/' namen.txt

Mit -E darf man die Klammern ohne Backslash schreiben. Im klassischen BRE-Modus müsste man \( und \) setzen — der Effekt ist derselbe, die Lesbarkeit leidet.

Bash Match in Klammern setzen
sed 's/[0-9]\+/[&]/g' datei.txt

& umschließt hier jede Zahl mit eckigen Klammern. Will man ein literales & im Replacement, schreibt man \&. Ebenso \\ für einen Backslash und \/ für einen Slash, wenn / der Delimiter ist.

-E Extended Regex

sed arbeitet per Default im BRE-Modus (Basic Regular Expressions). Dort sind +, ?, | und () literal — für ihre Metabedeutung braucht es Backslashes. Mit -E (auf manchen Systemen -r) wechselt sed in den ERE-Modus (Extended Regular Expressions), in dem diese Zeichen direkt als Metazeichen wirken — näher an dem, was man von grep -E, awk oder Perl gewohnt ist.

BREERE (-E)Bedeutung
\++Ein oder mehr
\??Null oder eins
|``
\(...\)(...)Capture Group
\{n,m\}{n,m}Quantifier-Bereich

Empfehlung: für alles außer trivialer Substitution -E nutzen. Die Skripte werden lesbarer und kompatibler zu dem, was man von anderen Werkzeugen kennt.

Bash ERE-Modus mit Alternative
sed -E 's/(GET|POST|PUT) /[\1] /g' access.log

Praxis-Patterns

Eine kuratierte Sammlung der sed-Einzeiler, die im Alltag immer wieder gebraucht werden. Jeder davon ist eine Vorlage zum Anpassen.

Bash Whitespace am Zeilenanfang und -ende trimmen
sed 's/^[[:space:]]*//;s/[[:space:]]*$//' datei.txt

[[:space:]] ist eine POSIX-Zeichenklasse für alle Whitespace-Zeichen — portabler als \s, das nicht in jedem sed-Dialekt verfügbar ist. Die zwei Substitutionen werden mit ; zu einem Skript verkettet.

Bash Block zwischen BEGIN und END extrahieren
sed -n '/^BEGIN/,/^END/p' datei.txt

-n schaltet den Default-Output ab, sodass nur das explizite p druckt. Die Adress-Range /^BEGIN/,/^END/ aktiviert den Druck-Befehl für alle Zeilen vom ersten Match auf BEGIN bis zum nächsten END — beide Grenzen inklusive.

Bash Mehrfache Leerzeilen zu einer reduzieren
sed '/^$/N;/^\n$/D' datei.txt

N hängt die nächste Zeile per Newline an, D löscht bis zur ersten Newline und startet das Skript neu — zusammen ergibt das den klassischen Squeeze-Trick für leere Zeilen.

Bash Nur erste Zeile mit Pattern ersetzen
sed '0,/X/{s/X/Y/}' datei.txt

Die Adress-Range 0,/X/ erfasst genau die Zeilen vom Dateianfang bis zum ersten Pattern-Treffer; die Substitution wirkt also nur dort. Trick: 0 als Startadresse ist eine GNU-Erweiterung — auf BSD/macOS muss man 1,/X/{s/X/Y/} schreiben, was aber fehlschlägt, wenn X schon in Zeile 1 steht.

Bash Zeilennummer voranstellen
sed = datei.txt | sed 'N;s/\n/\t/'

Der erste sed-Aufruf gibt vor jeder Zeile ihre Nummer aus, der zweite klebt Nummer und Zeile mit Tab zusammen. Vergleichbar mit nl, aber portabler.

Bash Letzte Zeile einer Datei löschen
sed -i '$d' datei.txt

$ ist die Adresse für die letzte Zeile, d löscht sie. Auf macOS muss -i ein leeres Argument bekommen: sed -i '' '$d' datei.txt, sonst sucht sed nach einem Backup-Suffix.

Bash Kommentare und Leerzeilen entfernen
sed -E '/^[[:space:]]*(#|$)/d' config.conf

Die Adresse matcht Zeilen, die — nach optionalem Whitespace — entweder mit # beginnen oder leer sind; d löscht sie aus dem Stream. Im Vergleich zu grep -Ev '^(#|$)' toleriert diese Variante eingerückte Kommentare wie # ... und ist damit robuster bei YAML- oder Python-artigen Configs.

Stolperfallen

-i auf macOS verlangt ein leeres Argument

Auf GNU-Linux funktioniert sed -i ‘s/…/…’ wie erwartet: in-place ohne Backup. Auf macOS und FreeBSD interpretiert sed das nächste Argument als Backup-Suffix — und fällt im schlimmsten Fall auf einen anderen Code-Pfad zurück, der die Datei nicht ändert oder mit kryptischer Fehlermeldung abbricht. Korrekt ist dort sed -i ” ‘s/…/…’ mit leeren Quotes. Wer portable Skripte schreibt, prüft uname oder nutzt perl -i -pe, das auf beiden Systemen identisch funktioniert.

Inline-Edit auf Symlinks ändert das Ziel, nicht den Symlink

sed -i schreibt eine neue Datei und benennt sie um — und folgt dabei einem Symlink zum Ziel. Wer sed -i ‘s/…/…’ /etc/some-symlink ausführt, ändert die echte Zieldatei, manchmal sogar mit veränderter Inode-Nummer. Bei Konfig-Dateien, die per Symlink in /etc liegen, kann das überraschen. Mit —follow-symlinks wird das Verhalten explizit.

BRE-Default — + und ? brauchen Backslash

Ohne -E ist sed im BRE-Modus, in dem + und ? als literale Zeichen gelten. sed ‘s/a+/X/’ matcht nur den Literalstring a+, nicht „ein oder mehr a”. Korrekt im BRE: sed ‘s/a+/X/’. Oder einfacher -E nutzen, dann gilt die ERE-Konvention. Diese Falle kostet erfahrungsgemäß die meisten Debugging-Minuten.

s///g matcht zu viel — Anker setzen

s/foo/bar/g ersetzt jedes Vorkommen, auch innerhalb längerer Woerter. foobar wird zu barbar, football zu barball. Wer das nicht will, setzt Wortgrenzen-Anker: s/\bfoo\b/bar/g (GNU) oder POSIX-konform s/[^a-zA-Z]foo[^a-zA-Z]/…/g. Vor Massen-Replacements lohnt ein Probelauf ohne -i oder mit diff-Vergleich.

Backslash-Escapes im Replacement nicht überall gleich

\n und \t im Replacement-Teil funktionieren in GNU-sed, in BSD-sed aber nicht zuverlässig — dort braucht es einen echten Newline (per Backslash am Zeilenende oder $‘\n’) bzw. ein echtes Tab-Zeichen. Wer portable Substitutionen schreibt, vermeidet solche Escapes oder testet auf beiden Systemen.

Hold Space ist Power, aber selten lesbar

Mit h, H, g, G und x kann sed theoretisch beliebige zustandsbehaftete Transformationen — Zeilen umsortieren, Dubletten entfernen, mehrzeilige Blöcke verarbeiten. In der Praxis sind solche Skripte schwer zu lesen und noch schwerer zu warten. Sobald Hold Space ins Spiel kommt, ist awk meistens die bessere Wahl, für alles dahinter Python.

sed ist zeilenorientiert — kein direktes Multiline-Match

sed arbeitet pro Zeile. Ein Pattern wie BEGIN.*END, das über zwei Zeilen geht, matcht nicht — der Pattern Space enthält zum Zeitpunkt des Matchings nur eine Zeile. Mit N kann man Zeilen anhängen, aber das wird schnell unleserlich. Für Multiline-Suchen sind grep -P mit -z, pcregrep, awk oder perl -0 die natürlicheren Werkzeuge.

Default-Output vs. -n und p

sed druckt jede Eingabezeile automatisch nach Abarbeitung des Skripts — der sogenannte Default-Output. p gibt zusätzlich aus, was zu Doppel-Zeilen führt. Mit -n wird der Default-Output unterdrückt, dann gibt nur p aus. Faustregel: Wer „nur die getroffenen Zeilen” haben will, schreibt sed -n ‘/PAT/p’ — das ist exakt das Verhalten von grep PAT.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Textverarbeitung

Zur Übersicht