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.
sed 's/alt/neu/g' datei.txtSubstitution 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.
| Flag | Wirkung |
|---|---|
g | Global — alle Treffer pro Zeile, nicht nur den ersten |
i / I | Case-insensitive (GNU-Erweiterung) |
1-N | Nur das N-te Match in der Zeile ersetzen |
p | Geänderte Zeile zusätzlich ausgeben (sinnvoll mit -n) |
w DATEI | Geänderte Zeile zusätzlich in DATEI schreiben |
e | Ergebnis 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.
sed 's/foo/bar/' datei.txtsed 's/foo/bar/gi' datei.txtDelimiter 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.
sed 's|/usr/local|/opt|g' config.txtMit 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.
sed 's,https://alt.example.com,https://neu.example.com,g' index.htmlInline-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.
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.
sed -i 's/alt/neu/g' datei.txtsed -i '' 's/alt/neu/g' datei.txtAdressen — 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.
| Adresse | Bedeutung |
|---|---|
5p | Nur Zeile 5 |
$d | Nur die letzte Zeile |
5,10d | Zeilen 5 bis 10 |
/PATTERN/d | Alle Zeilen, die PATTERN enthalten |
/START/,/END/p | Bereich von erstem START-Match bis erstem END-Match |
2,$d | Ab Zeile 2 bis Ende |
1~2 | Jede zweite Zeile ab Zeile 1 (GNU) |
5!d | Negation — 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.
sed -n '5p' datei.txtsed '/^#/d' config.confsed -n '/^BEGIN/,/^END/p' log.txtWichtige 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.
| Befehl | Wirkung |
|---|---|
s/X/Y/F | Substitution |
d | Pattern Space löschen, nächste Zeile lesen |
p | Pattern Space ausgeben (typisch mit -n) |
n | Nächste Zeile lesen, Pattern Space überschreiben |
N | Nächste Zeile anhängen (mit Newline dazwischen) |
i\TEXT | TEXT vor der Zeile einfügen |
a\TEXT | TEXT nach der Zeile anhängen |
c\TEXT | Zeile (oder Bereich) durch TEXT ersetzen |
= | Zeilennummer ausgeben |
q | Sofort beenden |
y/abc/xyz/ | Translation (zeichenweise, wie tr) |
h / H | Pattern Space in Hold Space kopieren / anhängen |
g / G | Hold Space in Pattern Space kopieren / anhängen |
x | Pattern 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.
sed 's/foo/FOO/g; s/bar/BAR/g' datei.txtsed '/ERROR/i\--- Fehler folgt ---' log.txtBackreferences
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.
sed -E 's/(\w+) (\w+)/\2 \1/' namen.txtMit -E darf man die Klammern ohne Backslash schreiben. Im klassischen BRE-Modus müsste man \( und \) setzen — der Effekt ist derselbe, die Lesbarkeit leidet.
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.
| BRE | ERE (-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.
sed -E 's/(GET|POST|PUT) /[\1] /g' access.logPraxis-Patterns
Eine kuratierte Sammlung der sed-Einzeiler, die im Alltag immer wieder gebraucht werden. Jeder davon ist eine Vorlage zum Anpassen.
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.
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.
sed '/^$/N;/^\n$/D' datei.txtN 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.
sed '0,/X/{s/X/Y/}' datei.txtDie 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.
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.
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.
sed -E '/^[[:space:]]*(#|$)/d' config.confDie 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
- sed(1) - Manpage (man7.org) - Vollständige Optionsreferenz von GNU-
sed - GNU sed Manual - Ausführliches Handbuch mit Beispielen, Hold Space und Skript-Files
- Arch Wiki: Core utilities - Kurznotizen zu
sedund Verwandten - Bruce Barnett: sed - An Introduction and Tutorial - Der klassische Tiefen-Tutorial mit allen Tricks
- sed one-liners (Eric Pement) - Sammlung legendaerer Einzeiler
Verwandte Artikel
- grep - Zeilen nach Pattern filtern - Suchen statt ändern
- awk - Feldweise Textverarbeitung - Wenn
sedzu komplex wird - tr - Zeichen ersetzen und löschen - Zeichenweise Transliteration
- find - Dateien rekursiv finden - Klassischer Partner für
sed -iper-exec - cut - Spalten extrahieren - Einfacher Feld-Extraktor