Sed: Unterschied zwischen den Versionen
K Textersetzung - „== Syntax ==“ durch „== Aufruf ==“ |
|||
(27 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 3: | Zeile 3: | ||
== Beschreibung == | == Beschreibung == | ||
* Da es ein "nicht-interaktives" Programm ist, kann es, falls gewünscht, zur Automatisierung der Bearbeitung verwendet werden. | '''sed''' steht für ''Stream EDitor'' und ist ein [[Unix]]-Werkzeug, mit dem Text-[[Datenstrom|Datenströme]] bearbeitet werden können. Der Datenstrom kann auch aus einer Datei gelesen werden. Im Gegensatz zu einem [[Texteditor]] wird die Ursprungsdatei aber nicht verändert. | ||
* Der Name sed ist eine Abkürzung für stream editor, und das Dienstprogramm leitet viele seiner Befehle vom ed line-editor ab (ed war der erste UNIX-Texteditor). | |||
* So können Sie mehrere Dateien bearbeiten oder gängige Bearbeitungsvorgänge durchführen, ohne jemals vi oder emacs öffnen zu müssen. | Im Gegensatz zu einem interaktiven Texteditor, wie etwa dem [[vi]], wird <code>sed</code> mittels eines [[Skriptsprache|Skripts]] gesteuert. | ||
* sed liest aus einer Datei oder von der Standardeingabe und gibt auf der Standardausgabe aus. | |||
* sed hat zwei Puffer, die Musterpuffer und Haltepuffer genannt werden. | Der <code>sed</code>-Befehlssatz orientiert sich an jenem des [[Zeilenorientierter Editor|zeilenorientierten Texteditors]] [[Ed (Texteditor)|ed]]. Dabei werden für die Text-Durchmusterung laut der [[Portable Operating System Interface|POSIX]]-Spezifikation eine bestimmte Abart der [[Regulärer Ausdruck|Regular Expressions]], sogenannte (POSIX-) ''Basic Regular Expressions'' (BRE) verwendet.<ref>{{Internetquelle |url=http://pubs.opengroup.org/onlinepubs/009695399/utilities/sed.html#tag_04_126_13_02 |titel=sed-Spezifikation der Open Group |sprache=en |zugriff=2013-03-27}}</ref> Die [[GNU]]-Implementation verwendet allerdings ''GNU-BRE''s, die von ''POSIX-BRE''s geringfügig abweichen. | ||
Auch wenn der Sprachumfang von <code>sed</code> ziemlich limitiert und spezialisiert erscheint, so handelt es sich doch um eine [[Turing-Vollständigkeit|Turing-vollständige]] Sprache. Beweisen kann man die Turing-Vollständigkeit, indem man eine [[Turingmaschine]] mittels <code>sed</code> programmiert<ref>{{Internetquelle |url=http://www.catonmat.net/ftp/sed/turing.txt |titel=Implementation of a Turing Machine as Sed Script |sprache=en |zugriff=2013-03-23}}</ref><ref>{{Internetquelle |url=http://www.stunet.tu-freiberg.de/~bahmann/turing-sed/ |titel=Turing-Maschine mit ''sed'' |sprache=de |zugriff=2013-03-17}}</ref> oder indem man mit ''sed'' einen Interpreter für eine andere, Turing-vollständige Sprache schreibt.<ref>{{Webarchiv|url=http://mi.eng.cam.ac.uk/~er258/code/sed/ |wayback=20100418131328 |text=cam.ac.uk}}</ref> | |||
Folglich konnten und wurden sogar Spiele wie [[Sokoban]] oder [[Arkanoid]] und andere anspruchsvolle Programme wie [[Debugger]] mit sed geschrieben.<ref>{{Internetquelle |url=http://sed.sourceforge.net/grabbag/scripts/#misc |titel=Liste verschiedener sed-Skripte |sprache=en |zugriff=2011-11-19}}</ref> | |||
; Sed ist ein Stromeditor (stream editor) | |||
* Ein Stromeditor wird für grundlegende Texttransformationen auf einen Eingabestrom (einer Datei oder aus einer Verarbeitungskette) verwandt. | |||
* Obwohl in einigen Aspekten ähnlich zu einem Editor, der Bearbeitungen nach Skript erlaubt (wie Ed), führt Sed nur einen Durchlauf über die Eingabe(n) durch und ist somit effizienter. | |||
* Allerdings ist es die Fähigkeit von Sed, Text in einer Verarbeitungskette zu filtern, die ihn besonders gegenüber anderen Arten von Editoren auszeichnet. | |||
* Da es ein "nicht-interaktives" Programm ist, kann es, falls gewünscht, zur Automatisierung der Bearbeitung verwendet werden. | |||
* Der Name sed ist eine Abkürzung für stream editor, und das Dienstprogramm leitet viele seiner Befehle vom ed line-editor ab (ed war der erste UNIX-Texteditor). | |||
* So können Sie mehrere Dateien bearbeiten oder gängige Bearbeitungsvorgänge durchführen, ohne jemals vi oder emacs öffnen zu müssen. | |||
* sed liest aus einer Datei oder von der Standardeingabe und gibt auf der Standardausgabe aus. | |||
* sed hat zwei Puffer, die Musterpuffer und Haltepuffer genannt werden. | |||
* Beide sind anfangs leer. | * Beide sind anfangs leer. | ||
; Funktionsweise | |||
* Geben Sie den Musterpuffer auf stdout aus. | * Geben Sie den Musterpuffer auf stdout aus. | ||
* Der Stream-Editor ist kein herkömmlicher Editor wie Vi oder Emacs. | * Der Stream-Editor ist kein herkömmlicher Editor wie Vi oder Emacs. | ||
* Sed arbeitet nicht interaktiv, sondern wird über Kommandozeilenoptionen oder ein Skript gesteuert. | * Sed arbeitet nicht interaktiv, sondern wird über Kommandozeilenoptionen oder ein Skript gesteuert. | ||
* Sed verändert nicht das Original, sondern schreibt das Ergebnis auf die Standardausgabe. | * Sed verändert nicht das Original, sondern schreibt das Ergebnis auf die Standardausgabe. | ||
* Sed lädt die aktuell betrachtete Zeile in einen temporären Puffer - im Folgenden als Arbeitspuffer bezeichnet. | * Sed lädt die aktuell betrachtete Zeile in einen temporären Puffer - im Folgenden als Arbeitspuffer bezeichnet. | ||
==== Unix Sed Arbeitsmethodik ==== | ==== Unix Sed Arbeitsmethodik ==== | ||
; | ; Ausführungszyklus | ||
* Der Zyklus wird fortgesetzt, bis das Ende der Datei/Eingabe erreicht ist # Lesen einer ganzen Zeile aus stdin/file | * Der Zyklus wird fortgesetzt, bis das Ende der Datei/Eingabe erreicht ist # Lesen einer ganzen Zeile aus stdin/file | ||
# Entfernt alle nachstehenden Zeilenumbrüche | # Entfernt alle nachstehenden Zeilenumbrüche | ||
# Legt die Zeile in seinen Musterpuffer | # Legt die Zeile in seinen Musterpuffer | ||
# Ändert den Musterpuffer entsprechend den übergebenen Befehlen | # Ändert den Musterpuffer entsprechend den übergebenen Befehlen | ||
==== Reguläre Ausdrücke ==== | ==== Reguläre Ausdrücke ==== | ||
; POSIX.2-BREs sollten unterstützt werden | ; POSIX.2-BREs sollten unterstützt werden | ||
* Dies erfolgt allerdings aus Leistungsgründen nicht vollständig. Die Sequenz \n in einem regulären Ausdruck passt auf das Zeilenumbruchzeichen und ähnliches gilt für \a, \t und andere Sequenzen. | * Dies erfolgt allerdings aus Leistungsgründen nicht vollständig. | ||
* Die Sequenz \n in einem regulären Ausdruck passt auf das Zeilenumbruchzeichen und ähnliches gilt für \a, \t und andere Sequenzen. | |||
* Die Option -E schaltet auf die Verwendung von erweiterten regulären Ausdrücken um; sie wurde seit Jahren in GNU Sed unterstützt und ist jetzt in POSIX enthalten. | * Die Option -E schaltet auf die Verwendung von erweiterten regulären Ausdrücken um; sie wurde seit Jahren in GNU Sed unterstützt und ist jetzt in POSIX enthalten. | ||
== Installation == | == Installation == | ||
# apt install sed | # '''apt install sed''' | ||
== | == Aufruf == | ||
sed [-V] [--version] [--help] [-n] [--quiet] [--silent] | $ '''sed''' [-V] [--version] [--help] [-n] [--quiet] [--silent] | ||
[-l N] [--line-length=N] [-u] [--unbuffered] | [-l N] [--line-length=N] [-u] [--unbuffered] | ||
[-E] [-r] [--regexp-extended] | [-E] [-r] [--regexp-extended] | ||
Zeile 82: | Zeile 98: | ||
[[Sed/Befehle]] | [[Sed/Befehle]] | ||
=== | === Rückgabewert === | ||
== | == Anwendungen == | ||
=== | === Arbeitsweise === | ||
<code>sed</code> kann sowohl innerhalb einer [[Pipe (Informatik)|Pipeline]] als auch auf [[Datei]]en arbeiten. Ausgaben erfolgen grundsätzlich auf <code><stdout></code>, Fehlermeldungen auf <code><stderr></code>. Der typische Aufruf sieht deshalb so aus: | |||
== | :{| | ||
| | |||
sed 'Anweisung1 | |||
Anweisung2 | |||
… | |||
Anweisung''N''' Eingabedatei > Ausgabedatei | |||
''<stream>'' | sed 'Anweisung1 | |||
Anweisung2 | |||
… | |||
Anweisung''N''' | ''<stream>'' | |||
|} | |||
<code>sed</code> liest eine Eingabedatei (oder einen <code>input stream</code> auf <code><stdin></code>) zeilenweise ein. Diese Eingangsdaten landen zunächst im sogenannten '''Pattern Space'''. Auf diesem Pattern Space wird nacheinander jede Anweisung des vorgegebenen Programmes ausgeführt. Jede dieser Anweisungen kann dabei den Pattern Space verändern, folgende Anweisungen werden dann auf dem jeweiligen Ergebnis der letzten Anweisung ausgeführt. Führt eine dieser Veränderungen zu einem Nulltext, so wird die Verarbeitung an dieser Stelle abgebrochen und die Anweisungsliste mit der nächsten Eingabezeile wieder von vorne begonnen. Ansonsten wird das Ergebnis der letzten Anweisung auf <code><stdout></code> ausgegeben und die Anweisungsliste ebenfalls mit der nächsten Eingabezeile wieder begonnen. | |||
=== Beispiele === | |||
# [[Sed/Aufruf]] | # [[Sed/Aufruf]] | ||
# [[Sed/Dateiübergabe]] | # [[Sed/Dateiübergabe]] | ||
Zeile 122: | Zeile 153: | ||
# http://sed.sf.net/grabbag/ | # http://sed.sf.net/grabbag/ | ||
{{Special:PrefixIndex/{{BASEPAGENAME}}}} | {{Special:PrefixIndex/{{BASEPAGENAME}}}} | ||
Zeile 128: | Zeile 159: | ||
# https://www.gnu.org/software/sed/manual/sed.html | # https://www.gnu.org/software/sed/manual/sed.html | ||
==== Man- | ==== Man-Page ==== | ||
* {{man|1|sed|posix|stream editor}} | |||
* {{man|1|sed|bsd|stream editor}} | |||
* {{man|1|sed|gnu|Stromeditor zum Filtern und Umwandeln von Text}} | |||
==== Info-Pages ==== | ==== Info-Pages ==== | ||
$ info sed | $ info sed | ||
=== Links === | === Links === | ||
==== Projekt ==== | ==== Projekt ==== | ||
==== Weblinks ==== | ==== Weblinks ==== | ||
# https://de.wikipedia.org/wiki/Sed_(Unix) | |||
# [http://sed.sourceforge.net/ sed-Projektseite auf sourceforge] (englisch) | |||
# [http://sed.sourceforge.net/grabbag/ seder’s grab bag] (englisch) | |||
# [http://www.pement.org/sed/ sed für Windows mit funktionierender -i Option] ([[ZIP-Dateiformat|ZIP]]; 50 kB) | |||
# [https://tty1.net/sed-tutorium/index.html Ausführliches Tutorium] (deutsch) | |||
== | = Wikipedia = | ||
== Programmierung == | |||
<code>sed</code>-Anweisungen können grob in drei Gruppen unterteilt werden: Textmanipulationen, Verzweigungen und sonstige. (Die meisten <code>sed</code>-Handbücher wie auch die POSIX-Spezifikation unterteilen abweichend davon Anweisungen in 2-Adress-, 1-Adress- und adresslose – siehe [[Sed#Textmanipulationen|unten]] –, aber diese Gruppierung ist für Einführungszwecke nicht geeignet.) | |||
< | |||
< | |||
</ | |||
[[Kategorie: | === Textmanipulationen === | ||
Dies ist die am weitaus häufigsten eingesetzte Funktion und der Befehlssatz ist hier auch besonders reichhaltig. Generell hat eine Anweisung folgende Struktur (''2-Adress-Kommando''): | |||
{| style="margin-left:2em" | |||
|- | |||
| | |||
<Adresse1>,<Adresse2> Kommando [Optionen] | |||
|} | |||
<code>Adresse1</code> und <code>Adresse2</code> können auch weggelassen werden. Werden beide Adressen angegeben, so wird <code>Kommando</code> für jede Zeile, beginnend mit jener, die mit <code>Adresse1</code> übereinstimmt, bis zu der, die mit <code>Adresse2</code> übereinstimmt, ausgeführt. Werden <code>Adresse1</code> und <code>Adresse2</code> nicht angegeben, so wird <code>Kommando</code> für jede Zeile ausgeführt, wird lediglich <code>Adresse2</code> weggelassen, so wird <code>Kommando</code> nur für Zeilen ausgeführt, die mit <code>Adresse1</code> übereinstimmen. Eine Adresse ist entweder eine Zeilennummer oder ein [[regulärer Ausdruck]]. Reguläre Ausdrücke werden dabei in zwei <code>/</code> eingeschlossen. Zwei Beispiele: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed '/Beginn/,/Ende/ s/alt/NEU/' inputfile | |||
|- | |||
|'''Input''' | |||
|'''Output''' | |||
|- | |||
| | |||
x alt | |||
Beginn | |||
y alt | |||
Ende | |||
z alt | |||
| | |||
x alt | |||
Beginn | |||
y NEU | |||
Ende | |||
z alt | |||
|} | |||
„alt“ wird durch „NEU“ ersetzt, aber nur ab der Zeile, die „Beginn“ enthält, bis zu der Zeile, die „Ende“ enthält (2-Adress-Variante). Hingegen wird dieselbe Ersetzung im zweiten Beispiel in allen Zeilen durchgeführt, die mit „y“ oder „z“ beginnen (1-Adress-Variante): | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed '/^[yz]/ s/alt/NEU/' inputfile | |||
|- | |||
|'''Input''' | |||
|'''Output''' | |||
|- | |||
| | |||
x alt | |||
Beginn | |||
y alt | |||
Ende | |||
z alt | |||
| | |||
x alt | |||
Beginn | |||
y NEU | |||
Ende | |||
z NEU | |||
|} | |||
==== Zusammengesetzte Kommandos ==== | |||
Anstatt eines einzelnen Kommandos kann <code>Kommando</code> auch eine Liste von Anweisungen enthalten, die durch <code>{ … }</code> umschlossen werden. Für diese Anweisungen gelten wieder die oben beschriebenen Regeln, sie können ihrerseits ebenfalls aus weiteren zusammengesetzten Kommandos bestehen. Ein Beispiel: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed '/^[yz]/ { | |||
s/^\([yz]\)/(\1)/ | |||
s/alt/NEU/ | |||
}' inputfile | |||
|- | |||
|'''Input''' | |||
|'''Output''' | |||
|- | |||
| | |||
x alt | |||
Beginn | |||
y alt | |||
Ende | |||
z alt | |||
| | |||
x alt | |||
Beginn | |||
(y) NEU | |||
Ende | |||
(z) NEU | |||
|} | |||
=== Verzweigungen === | |||
<code>sed</code> kennt zwei Arten von Verzweigungen: unbedingte Verzweigungen (Sprunganweisungen) und bedingte, die in Abhängigkeit einer zuvor erfolgten oder nicht erfolgten Ersetzungsoperation zur Ausführung kommen. Ein typisches Anwendungsbeispiel ist das folgende: ein Quelltext wurde mit Hilfe von führenden [[Tabulatorzeichen]] eingerückt, diese führenden ''Tabs'' sollen durch jeweils 8 ''Blanks'' ersetzt werden. Andere als am Zeilenbeginn liegende ''Tabs'' können im Text vorkommen, sollen aber nicht verändert werden. Das Problem besteht darin, dass multiplikative Verknüpfungen (''ersetze N Tabs durch N * 8 Blanks'') nicht als RegExp ausgedrückt werden können. Andererseits würde eine globale Ersetzung auch die Tabulatorzeichen innerhalb des Texts betreffen. Deshalb wird mit Sprunganweisungen eine Schleife gebildet (im Folgenden werden ''Blanks'' und ''Tabs'' zur besseren Verständlichkeit durch <code><b></code> und <code><t></code> symbolisiert): | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed ':start | |||
/^<b>*<t>/ { | |||
s/^\(<b>*\)<t>/\1<b><b><b><b><b><b><b><b>/ | |||
b start | |||
}' inputfile | |||
|} | |||
In jeder Zeile wird das erste Tabulatorzeichen, sofern davor lediglich null oder mehr Leerzeichen stehen, durch 8 Leerzeichen ersetzt, danach sorgt die Sprunganweisung <code>b <Sprungzielname></code> dafür, dass die Programmausführung wieder zur ersten Zeile zurückkehrt. Ist das letzte führende Tabulatorzeichen ersetzt, so matcht der Ausdruck <code>/^<b>*<t>/</code> nicht mehr und der Block wird nicht ausgeführt, sodass das Programmende erreicht und die nächste Zeile eingelesen wird. | |||
Hier wird die Ähnlichkeit mit Assembler-Sprachen deutlich, indem mit einer Bedingung und einem [[Label (Programmierung)|Label]] eine Kontrollstruktur vergleichbar dem in Hochsprachen üblichen <code style="white-space:nowrap">repeat-until</code> aufgebaut wird. | |||
=== Sonstige Anweisungen === | |||
==== Hold Space Manipulation ==== | |||
Eine mächtige (gleichwohl relativ unbekannte) Funktion von <code>sed</code> ist der sogenannte '''Hold Space'''. Das ist ein frei verfügbarer Speicherbereich, der in seiner Arbeitsweise dem in manchen [[Assemblersprache|Assembler-Sprachen]] bekannten [[Akkumulator (Computer)|Akkumulator]] ähnelt. Direkte Manipulation der Daten im Hold Space ist zwar nicht möglich, aber Daten im Pattern Space können in den Hold Space verlagert, kopiert, oder auch mit dem Inhalt desselben vertauscht werden. Auch das Anhängen des Pattern Spaces an den Hold Space oder vice versa ist möglich. | |||
Das folgende Beispiel verdeutlicht die Funktion des Hold Space: der Text einer „Kapitelüberschrift“ wird gespeichert und jeder Zeile des jeweiligen „Kapitels“ nachgestellt, die Zeile mit der Kapitelüberschrift selbst aber unterdrückt: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed '/^=/ { | |||
s/^=// | |||
s/^/ (/ | |||
s/$/)/ | |||
h | |||
d | |||
} | |||
G; s/\n// ' inputfile | |||
|- | |||
|'''Input''' | |||
|'''Output''' | |||
|- | |||
| | |||
=Kapitel1 | |||
Zeile 1 | |||
Zeile 2 | |||
Zeile 3 | |||
=Kapitel2 | |||
Zeile A | |||
Zeile B | |||
Zeile C | |||
| | |||
Zeile 1 (Kapitel1) | |||
Zeile 2 (Kapitel1) | |||
Zeile 3 (Kapitel1) | |||
Zeile A (Kapitel2) | |||
Zeile B (Kapitel2) | |||
Zeile C (Kapitel2) | |||
|} | |||
Immer wenn eine Zeile mit „=“ beginnt, so wird der Anweisungsblock ausgeführt, der dieses Zeichen entfernt und dafür die restliche Zeile mit einem führenden Leerzeichen und Klammern versieht. Danach wird dieser Text in den Hold Space kopiert (<code>h</code>) und aus dem Pattern Space gelöscht (<code>d</code>), wodurch das Programm für diese Zeile beendet und die nächste Zeile gelesen wird. Da für „normale Zeilen“ die Bedingung des Eingangsblocks nicht zutrifft, wird lediglich die letzte Anweisung (<code>G</code>) durchgeführt, die den Inhalt des Hold Space an den Pattern Space anhängt. | |||
==== Mehrzeilen-Anweisungen ==== | |||
Nicht alle Textmanipulationen lassen sich innerhalb einzelner Zeilen ausführen. Manchmal müssen Informationen aus anderen Zeilen in die Entscheidungsfindung miteinbezogen werden, manchmal auch zeilenübergreifende Ersetzungen durchgeführt werden. Dafür sieht die <code>sed</code>-Programmiersprache die Anweisungen <code>N</code>, <code>P</code> und <code>D</code> vor, mit denen mehrere Zeilen des Eingabetexts gleichzeitig in den Pattern Space geladen (<code>N</code>) und Teile davon ausgegeben (<code>P</code>) oder gelöscht (<code>D</code>) werden können. Ein typisches Anwendungsbeispiel ist der folgende Einzeiler (eigentlich zwei Einzeiler), der einen Text mit Zeilennummern versieht: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed '=' inputfile | sed 'N; s/\n/<t>/' | |||
|} | |||
Der erste <code>sed</code>-Aufruf druckt für jede Zeile im Eingangstext die Zeilennummer aus und danach die Zeile selbst. Der zweite <code>sed</code>-Aufruf verbindet diese beiden Zeilen zu einer einzigen, indem erst die jeweils nachfolgende Zeile eingelesen (<code>N</code>) und dann das automatisch eingefügte Zeilentrennzeichen („\n“) durch ein Tabulatorzeichen ersetzt wird. | |||
== Anwendungen, Optionen, Hinweise == | |||
=== Kapazitätsgrenzen === | |||
<code>sed</code> unterliegt keinen (realen) Beschränkungen hinsichtlich der Dateigrößen. Abgesehen vom verfügbaren Plattenplatz, der eine praktische Grenze darstellt, realisieren die meisten Implementationen den Zeilenzähler als <code>int</code> oder <code>long int</code>. Bei den heute üblichen 64-Bit-Prozessoren kann die Gefahr eines Überlaufs deshalb vernachlässigt werden. | |||
Wie die meisten textmanipulierenden Tools in UNIX unterliegt <code>sed</code> allerdings einer Begrenzung hinsichtlich der Zeilenlänge (genauer: der Anzahl Bytes bis zum nachfolgenden <code>newline</code>-Zeichen). Die Mindestgröße ist durch den POSIX-Standard festgelegt, die tatsächliche Größe kann von System zu System variieren und kann im jeweiligen Fall in der Kernel-Headerdatei <code>/usr/include/limits.h</code> als Wert der Konstanten <code>LINE_MAX</code> nachgeschlagen werden. Die Länge wird in ''Bytes'' angegeben, nicht in ''Zeichen'' (weshalb eine Umrechnung etwa bei der Verarbeitung von UTF-codierten Dateien, die einzelne Zeichen mit mehreren Bytes darstellen, nötig ist). | |||
=== Greedyness === | |||
{{Siehe auch|Regulärer Ausdruck}} | |||
Beim Geltungsbereich von <code>RegExp</code>s wird zwischen ''greedy'' und ''non-greedy'' unterschieden. <code>sed</code>-<code>RegExp</code>s sind immer ''greedy'', das bedeutet, dass die <code>RegExp</code> immer den längstmöglichen Geltungsbereich hat: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
/a.*B/; "'a', gefolgt von null oder mehr beliebigen Zeichen, gefolgt von 'B'" | |||
'''axyBBBskdjfhaaBBpweruB'''jdfh ; längstmöglicher Geltungsbereich (greedy) | |||
'''axyB'''BBskdjfhaaBBpweruBjdfh ; kürzestmöglicher Geltungsbereich (non-greedy) | |||
|} | |||
Der Grund ist, dass <code>sed</code> auf Geschwindigkeit optimiert ist und non-greedy <code>RegExp</code>s aufwendiges [[Backtracking]] erfordern würde. Will man ein Non-greedy-Verhalten erzwingen, so erreicht man dies üblicherweise durch negierte Zeichenklassen. Im obigen Beispiel etwa: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
/a[^B]*B/ ; "'a', gefolgt von null oder mehr nicht-'B', gefolgt von 'B'" | |||
|} | |||
=== Praktische Grenzen in der Shell-Programmierung === | |||
Es sollte nicht unerwähnt bleiben, dass die allerhäufigste Anwendung von <code>sed</code> (aber auch von <code>[[awk]]</code>, <code>[[Tr (Unix)|tr]]</code> und ähnlichen Filterprogrammen) in der Praxis – die Manipulation ''ad hoc'' von Ausgaben anderer Kommandos, etwa so: | |||
{| style="margin-left:2em" | |||
|- | |||
| | |||
ls -l /path/to/myfile | sed 's/^\([^ ][^ ]*\) .*/\1/' # gibt Filetype und Filemode aus | |||
|} | |||
genaugenommen einen Missbrauch darstellt. Da jeder Aufruf eines externen Programmes die aufwendigen Systemaufrufe <code>[[Fork (Unix)#Nutzung von Fork zum Starten anderer Programme|fork()]]</code> und <code>exec()</code> erfordert, sind Shell-interne Methoden, etwa die sogenannte ''Variablenexpansion'', selbst wenn sie deutlich länger zu schreiben sind, meist dem Aufruf von externen Programmen überlegen.<ref>{{Internetquelle |url=http://www.unix.com/302248917-post19.html |titel=Comparing the Run-Time Efficiency of a ROT13 Algorithm in tr vs. ksh93 |sprache=en |zugriff=2013-03-25}}</ref> Die Faustregel dafür lautet: wenn die Ausgabe des Filterprozesses eine Datei bzw. ein Datenstrom ist, so ist das Filterprogramm zu verwenden, ansonsten ist Variablenexpansion vorzuziehen. | |||
=== In-Place-Editing === | |||
Aufgrund der Art wie <code>sed</code> Textmanipulationen durchführt, kann dies nicht direkt auf der Eingabedatei geschehen. Als Ausgabe wird eine von dieser getrennte Datei benötigt, die gegebenenfalls danach über die Eingangsdatei kopiert wird. | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed '…<Anweisungen>…' /path/to/inputfile > /path/to/output | |||
mv /path/to/output /path/to/input | |||
|} | |||
Dies ist auch so im POSIX-Standard vorgesehen. Die GNU-Version von sed bietet zusätzlich zum POSIX-Standard die Kommandozeilen-Option <code>-i</code>. Diese erlaubt es, eine Datei scheinbar ohne Umweg (''in place'') zu verändern, tatsächlich wird aber im Hintergrund ebenfalls eine temporäre Datei angelegt. Diese wird im Fehlerfall nicht gelöscht und die Metadaten (Besitzer, Gruppe, Inode-Nummer, …) der Originaldatei auf jeden Fall verändert. | |||
=== <code>RegExp</code>-Notation === | |||
Es hat sich eingebürgert, ''regular Expressions'' – wie auch in den obigen Beispielen – durch Schrägstriche zu begrenzen. <code>sed</code> erfordert dies allerdings nicht. Jedes Zeichen, das einem Ersetzungskommando folgt, wird als Begrenzer akzeptiert und dann in der Folge erwartet. Diese beiden Anweisungen sind deshalb gleichwertig: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
s/^\([^ ][^ ]*\) \([^ ][^ ]*\)/\2 \1/ ; vertauscht erstes und zweites Wort einer Zeile | |||
s_^\([^ ][^ ]*\) \([^ ][^ ]*\)_\2 \1_ ; "_" statt "/" | |||
|} | |||
Dies ist praktisch, wenn der Schrägstrich als Teil der <code>RegExp</code> benötigt wird, weil man sich dann das mühsame ''Escapen'' (Kenntlichmachen der Verwendung als [[Literal]]) ersparen kann. Man weicht dann einfach auf ein anderes, nicht verwendetes Zeichen aus. | |||
=== Einige typische Verfahren === | |||
==== Löschung von Textteilen ==== | |||
Erfolgt durch ''Ersetzung durch nichts''. Explizite Löschung für Teile einer Zeile ist nur vom Zeilenbeginn bis zum ersten Zeilentrennzeichen (<code>D</code>) vorgesehen. Der Ausdruck | |||
{| style="margin-left:2em" | |||
|- | |||
| | |||
/Ausdruck/d | |||
|} | |||
löscht hingegen '''NICHT''' den Teil ''Ausdruck'', sondern jede Zeile, die ''Ausdruck'' enthält! ''Ausdruck'' fungiert hier als die Adresse (siehe oben, 1-Adress-Variante des Kommandos <code>d</code>). | |||
==== Ansprechen von mindestens einem Zeichen ==== | |||
Im Umfang der POSIX-BREs ist – im Unterschied zu den GNU-BREs – der [[Regexp#Quantoren|Quantor]] <code>\+</code> für ''ein oder mehrere des vorangegangenen Ausdrucks'' nicht vorgesehen. Um portable <code>sed</code>-Scripte zu schreiben, die nicht nur mit GNU-sed laufen, sollte der [[Regexp#Quantoren|Quantor]] <code>\{min,\}</code> verwendet werden. | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
/xa\+y/ ; GNU-Variante für "'x' gefolgt von einem oder mehr (aber nicht null) 'a', gefolgt von 'y'" | |||
/xa\{1,\}y/ ; dasselbe in POSIX: "'x' gefolgt von mindestens einem 'a', gefolgt von 'y'" | |||
|} | |||
==== Ersetzung mehrerer bzw. aller Vorkommen innerhalb einer Zeile ==== | |||
Ohne Angabe weiterer Optionen wird immer nur das erste Vorkommen eines Suchtexts der Ersetzungsregel unterworfen: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed 's/alt/NEU/' inputfile | |||
|- | |||
|'''Input''' | |||
|'''Output''' | |||
|- | |||
| | |||
alt | |||
alt alt | |||
alt alt alt | |||
alt alt alt alt | |||
alt alt alt alt alt | |||
| | |||
NEU | |||
NEU alt | |||
NEU alt alt | |||
NEU alt alt alt | |||
NEU alt alt alt alt | |||
|} | |||
Dieses Verhalten kann allerdings durch die Angabe von Kommandoptionen geändert werden: Wird eine Zahl N angegeben, dann wird lediglich das ''N''-te Vorkommen geändert, ein <code>g</code> (für ''global'') ändert alle Vorkommen: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed 's/alt/NEU/'''g'''' inputfile | |||
|- | |||
|'''Input''' | |||
|'''Output''' | |||
|- | |||
| | |||
alt | |||
alt alt | |||
alt alt alt | |||
alt alt alt alt | |||
alt alt alt alt alt | |||
| | |||
NEU | |||
NEU NEU | |||
NEU NEU NEU | |||
NEU NEU NEU NEU | |||
NEU NEU NEU NEU NEU | |||
|} | |||
==== Filtern bestimmter Zeilen ==== | |||
Grundsätzlich gibt <code>sed</code> immer den Inhalt des Pattern Spaces nach der letzten Anweisung aus. Will man dieses Verhalten für einzelne Zeilen unterdrücken, so kann man entweder über eine Regel bestimmte Zeilen löschen (explizite Filterung), aber es ist auch mit der Kommandozeilen-Option <code>-n</code> möglich, dieses Verhalten insgesamt abzustellen (implizite Filterung). Ausgegeben wird dann nur noch, was mit dem ausdrücklichen <code>Print</code>-Kommando (<code>p</code>) angegeben wird. <code>p</code> kann dabei entweder als eigene Anweisung oder als Option für andere Anweisungen dienen. Das Beispiel gibt aus dem bereits oben verwendeten Text nur noch die „Kapitelüberschriften“ aus: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed -n 's/^=\(.*\)$/Kapitelüberschrift: \1/p' inputfile | |||
|- | |||
|'''Input''' | |||
|'''Output''' | |||
|- | |||
| | |||
=Kapitel1 | |||
Zeile 1 | |||
Zeile 2 | |||
Zeile 3 | |||
=Kapitel2 | |||
Zeile A | |||
Zeile B | |||
Zeile C | |||
| | |||
Kapitelüberschrift: Kapitel1 | |||
Kapitelüberschrift: Kapitel2 | |||
|} | |||
==== Debugging ==== | |||
Zur Fehlersuche kann es nützlich sein, sich Zwischenergebnisse ausgeben zu lassen um die Entwicklung im Pattern Space besser nachvollziehen zu können. Dazu kann die bereits oben erwähnte Option <code>p</code> verwendet werden. Zeilen können durchaus mehrmals hintereinander ausgegeben werden. In dem obigen Beispielprogramm etwa: | |||
{| style="margin-left:2em" | |||
|- | |||
|colspan="2"| | |||
sed '/^=/ { | |||
s/^=//'''p''' | |||
s/^/ (/'''p''' | |||
s/$/)/'''p''' | |||
h | |||
d | |||
} | |||
'''p''' | |||
G' inputfile | |||
|} | |||
[[Kategorie:Sed]] | |||
[[Kategorie:Linux/Befehl]] | [[Kategorie:Linux/Befehl]] | ||
{{DEFAULTSORT:sed}} | {{DEFAULTSORT:sed}} |
Aktuelle Version vom 12. November 2024, 18:36 Uhr
sed - Stromeditor zum Filtern und Umwandeln von Text
Beschreibung
sed steht für Stream EDitor und ist ein Unix-Werkzeug, mit dem Text-Datenströme bearbeitet werden können. Der Datenstrom kann auch aus einer Datei gelesen werden. Im Gegensatz zu einem Texteditor wird die Ursprungsdatei aber nicht verändert.
Im Gegensatz zu einem interaktiven Texteditor, wie etwa dem vi, wird sed
mittels eines Skripts gesteuert.
Der sed
-Befehlssatz orientiert sich an jenem des zeilenorientierten Texteditors ed. Dabei werden für die Text-Durchmusterung laut der POSIX-Spezifikation eine bestimmte Abart der Regular Expressions, sogenannte (POSIX-) Basic Regular Expressions (BRE) verwendet.[1] Die GNU-Implementation verwendet allerdings GNU-BREs, die von POSIX-BREs geringfügig abweichen.
Auch wenn der Sprachumfang von sed
ziemlich limitiert und spezialisiert erscheint, so handelt es sich doch um eine Turing-vollständige Sprache. Beweisen kann man die Turing-Vollständigkeit, indem man eine Turingmaschine mittels sed
programmiert[2][3] oder indem man mit sed einen Interpreter für eine andere, Turing-vollständige Sprache schreibt.[4]
Folglich konnten und wurden sogar Spiele wie Sokoban oder Arkanoid und andere anspruchsvolle Programme wie Debugger mit sed geschrieben.[5]
- Sed ist ein Stromeditor (stream editor)
- Ein Stromeditor wird für grundlegende Texttransformationen auf einen Eingabestrom (einer Datei oder aus einer Verarbeitungskette) verwandt.
- Obwohl in einigen Aspekten ähnlich zu einem Editor, der Bearbeitungen nach Skript erlaubt (wie Ed), führt Sed nur einen Durchlauf über die Eingabe(n) durch und ist somit effizienter.
- Allerdings ist es die Fähigkeit von Sed, Text in einer Verarbeitungskette zu filtern, die ihn besonders gegenüber anderen Arten von Editoren auszeichnet.
- Da es ein "nicht-interaktives" Programm ist, kann es, falls gewünscht, zur Automatisierung der Bearbeitung verwendet werden.
- Der Name sed ist eine Abkürzung für stream editor, und das Dienstprogramm leitet viele seiner Befehle vom ed line-editor ab (ed war der erste UNIX-Texteditor).
- So können Sie mehrere Dateien bearbeiten oder gängige Bearbeitungsvorgänge durchführen, ohne jemals vi oder emacs öffnen zu müssen.
- sed liest aus einer Datei oder von der Standardeingabe und gibt auf der Standardausgabe aus.
- sed hat zwei Puffer, die Musterpuffer und Haltepuffer genannt werden.
- Beide sind anfangs leer.
- Funktionsweise
- Geben Sie den Musterpuffer auf stdout aus.
- Der Stream-Editor ist kein herkömmlicher Editor wie Vi oder Emacs.
- Sed arbeitet nicht interaktiv, sondern wird über Kommandozeilenoptionen oder ein Skript gesteuert.
- Sed verändert nicht das Original, sondern schreibt das Ergebnis auf die Standardausgabe.
- Sed lädt die aktuell betrachtete Zeile in einen temporären Puffer - im Folgenden als Arbeitspuffer bezeichnet.
Unix Sed Arbeitsmethodik
- Ausführungszyklus
- Der Zyklus wird fortgesetzt, bis das Ende der Datei/Eingabe erreicht ist # Lesen einer ganzen Zeile aus stdin/file
- Entfernt alle nachstehenden Zeilenumbrüche
- Legt die Zeile in seinen Musterpuffer
- Ändert den Musterpuffer entsprechend den übergebenen Befehlen
Reguläre Ausdrücke
- POSIX.2-BREs sollten unterstützt werden
- Dies erfolgt allerdings aus Leistungsgründen nicht vollständig.
- Die Sequenz \n in einem regulären Ausdruck passt auf das Zeilenumbruchzeichen und ähnliches gilt für \a, \t und andere Sequenzen.
- Die Option -E schaltet auf die Verwendung von erweiterten regulären Ausdrücken um; sie wurde seit Jahren in GNU Sed unterstützt und ist jetzt in POSIX enthalten.
Installation
# apt install sed
Aufruf
$ sed [-V] [--version] [--help] [-n] [--quiet] [--silent] [-l N] [--line-length=N] [-u] [--unbuffered] [-E] [-r] [--regexp-extended] [-e Skript] [--expression=script] [-f Skriptdatei] [--file=Skriptdatei] [script-if-no-other-script] [Datei …]
Optionen
Unix | GNU | Beschreibung |
---|---|---|
-n | --quiet, --silent | unterdrückt die Ausgabe des Musterbereichs |
--debug | Kommentiert die Programmausführung | |
-e Skript | --expression=Skript | Fügt das Skript zu den auszuführenden Befehlen hinzu |
-f Skriptdatei | --file=Skriptdatei | Fügt den Inhalt der Skriptdatei zu den auszuführenden Befehlen hinzu |
--follow-symlinks | Symbolischen Links bei der Bearbeitung an Ort und Stelle folgen | |
-i[ENDUNG] | --in-place[=ENDUNG] | Dateien an Ort und Stelle bearbeiten (erstellt Sicherung, falls ENDUNG angegeben) |
-l N | --line-length=N | Gibt die gewünschte Zeilenumbruchlänge für den Befehl »l« an |
--posix | Deaktiviert alle GNU-Erweiterungen | |
-E, -r | --regexp-extended | Verwendet erweiterte reguläre Ausdrücke in dem Skript (für Portabilität verwenden Sie POSIX -E) |
-s | --separate | Betrachtet Dateien getrennt statt als einzigen, kontinuierlichen langen Strom |
--sandbox | Arbeitet im »Sandbox«-Modus (deaktiviert e/r/w-Befehle) | |
-u | --unbuffered | Lädt minimale Datenmenge aus den Eingabedateien und schreibt die Ausgabepuffer öfter |
-z | --null-data | Zeilen durch NUL-Zeichen trennen |
--help | zeigt Hilfeinformationen an und beendet das Programm | |
--version | gibt Versionsinformationen aus und beendet das Programm |
Befehle
Rückgabewert
Anwendungen
Arbeitsweise
sed
kann sowohl innerhalb einer Pipeline als auch auf Dateien arbeiten. Ausgaben erfolgen grundsätzlich auf <stdout>
, Fehlermeldungen auf <stderr>
. Der typische Aufruf sieht deshalb so aus:
sed 'Anweisung1 Anweisung2 … AnweisungN' Eingabedatei > Ausgabedatei <stream> | sed 'Anweisung1 Anweisung2 … AnweisungN' | <stream>
sed
liest eine Eingabedatei (oder einen input stream
auf <stdin>
) zeilenweise ein. Diese Eingangsdaten landen zunächst im sogenannten Pattern Space. Auf diesem Pattern Space wird nacheinander jede Anweisung des vorgegebenen Programmes ausgeführt. Jede dieser Anweisungen kann dabei den Pattern Space verändern, folgende Anweisungen werden dann auf dem jeweiligen Ergebnis der letzten Anweisung ausgeführt. Führt eine dieser Veränderungen zu einem Nulltext, so wird die Verarbeitung an dieser Stelle abgebrochen und die Anweisungsliste mit der nächsten Eingabezeile wieder von vorne begonnen. Ansonsten wird das Ergebnis der letzten Anweisung auf <stdout>
ausgegeben und die Anweisungsliste ebenfalls mit der nächsten Eingabezeile wieder begonnen.
Beispiele
- Sed/Aufruf
- Sed/Dateiübergabe
- Sed/Ausgabe
- Sed/Kommandos
- Sed/Substitutionen
- Sed/Regular Expressions
- Sed/Beispieltext
- Sed/Anweisungen
- Sed/Zeilen ausgeben
- Sed/Adressierung
- Sed/Zeilen löschen
- Sed/Suchen und Ersetzen
- Sed/Mehrere Kommandos
- Sed/Einfügen
- Sed/Einfügen aus einer Datei
- Sed/Schreiben in eine Datei
- Sed/Dateien direkt bearbeiten
- Sed/Dateien ergänzen
- Sed/Nächste Zeile beabeiten
- Sed/Zeichen tauschen
- Sed/Sed vorzeitig beenden
- Sed/Zeilentausch
- Sed/Sed-Skripte
Siehe auch
- awk(1)
- ed(1)
- grep(1)
- tr(1)
- perlre(1)
- sed.info
- Sed-FAQ http://sed.sf.net/grabbag/tutorials/sedfaq.txt
- http://sed.sf.net/grabbag/
- Sed
- Sed/Adressierung
- Sed/Anweisungen
- Sed/Aufruf
- Sed/Ausgabe
- Sed/Befehle
- Sed/Beispieltext
- Sed/Dateien direkt bearbeiten
- Sed/Dateien ergänzen
- Sed/Dateiübergabe
- Sed/Einfügen
- Sed/Einfügen aus einer Datei
- Sed/Kommandos
- Sed/Mehrere Kommandos
- Sed/Nächste Zeile beabeiten
- Sed/Regular Expressions
- Sed/Schreiben in eine Datei
- Sed/Sed-Skripte
- Sed/Sed vorzeitig beenden
- Sed/Substitutionen
- Sed/Suchen und Ersetzen
- Sed/Zeichen tauschen
- Sed/Zeilen ausgeben
- Sed/Zeilen löschen
- Sed/Zeilentausch
Dokumentation
Man-Page
Info-Pages
$ info sed
Links
Projekt
Weblinks
- https://de.wikipedia.org/wiki/Sed_(Unix)
- sed-Projektseite auf sourceforge (englisch)
- seder’s grab bag (englisch)
- sed für Windows mit funktionierender -i Option (ZIP; 50 kB)
- Ausführliches Tutorium (deutsch)
Wikipedia
Programmierung
sed
-Anweisungen können grob in drei Gruppen unterteilt werden: Textmanipulationen, Verzweigungen und sonstige. (Die meisten sed
-Handbücher wie auch die POSIX-Spezifikation unterteilen abweichend davon Anweisungen in 2-Adress-, 1-Adress- und adresslose – siehe unten –, aber diese Gruppierung ist für Einführungszwecke nicht geeignet.)
Textmanipulationen
Dies ist die am weitaus häufigsten eingesetzte Funktion und der Befehlssatz ist hier auch besonders reichhaltig. Generell hat eine Anweisung folgende Struktur (2-Adress-Kommando):
<Adresse1>,<Adresse2> Kommando [Optionen] |
Adresse1
und Adresse2
können auch weggelassen werden. Werden beide Adressen angegeben, so wird Kommando
für jede Zeile, beginnend mit jener, die mit Adresse1
übereinstimmt, bis zu der, die mit Adresse2
übereinstimmt, ausgeführt. Werden Adresse1
und Adresse2
nicht angegeben, so wird Kommando
für jede Zeile ausgeführt, wird lediglich Adresse2
weggelassen, so wird Kommando
nur für Zeilen ausgeführt, die mit Adresse1
übereinstimmen. Eine Adresse ist entweder eine Zeilennummer oder ein regulärer Ausdruck. Reguläre Ausdrücke werden dabei in zwei /
eingeschlossen. Zwei Beispiele:
sed '/Beginn/,/Ende/ s/alt/NEU/' inputfile | |
Input | Output |
x alt Beginn y alt Ende z alt |
x alt Beginn y NEU Ende z alt |
„alt“ wird durch „NEU“ ersetzt, aber nur ab der Zeile, die „Beginn“ enthält, bis zu der Zeile, die „Ende“ enthält (2-Adress-Variante). Hingegen wird dieselbe Ersetzung im zweiten Beispiel in allen Zeilen durchgeführt, die mit „y“ oder „z“ beginnen (1-Adress-Variante):
sed '/^[yz]/ s/alt/NEU/' inputfile | |
Input | Output |
x alt Beginn y alt Ende z alt |
x alt Beginn y NEU Ende z NEU |
Zusammengesetzte Kommandos
Anstatt eines einzelnen Kommandos kann Kommando
auch eine Liste von Anweisungen enthalten, die durch { … }
umschlossen werden. Für diese Anweisungen gelten wieder die oben beschriebenen Regeln, sie können ihrerseits ebenfalls aus weiteren zusammengesetzten Kommandos bestehen. Ein Beispiel:
sed '/^[yz]/ { s/^\([yz]\)/(\1)/ s/alt/NEU/ }' inputfile | |
Input | Output |
x alt Beginn y alt Ende z alt |
x alt Beginn (y) NEU Ende (z) NEU |
Verzweigungen
sed
kennt zwei Arten von Verzweigungen: unbedingte Verzweigungen (Sprunganweisungen) und bedingte, die in Abhängigkeit einer zuvor erfolgten oder nicht erfolgten Ersetzungsoperation zur Ausführung kommen. Ein typisches Anwendungsbeispiel ist das folgende: ein Quelltext wurde mit Hilfe von führenden Tabulatorzeichen eingerückt, diese führenden Tabs sollen durch jeweils 8 Blanks ersetzt werden. Andere als am Zeilenbeginn liegende Tabs können im Text vorkommen, sollen aber nicht verändert werden. Das Problem besteht darin, dass multiplikative Verknüpfungen (ersetze N Tabs durch N * 8 Blanks) nicht als RegExp ausgedrückt werden können. Andererseits würde eine globale Ersetzung auch die Tabulatorzeichen innerhalb des Texts betreffen. Deshalb wird mit Sprunganweisungen eine Schleife gebildet (im Folgenden werden Blanks und Tabs zur besseren Verständlichkeit durch <b>
und <t>
symbolisiert):
sed ':start /^<b>*<t>/ { s/^\(<b>*\)<t>/\1<b><b><b><b><b><b><b><b>/ b start }' inputfile |
In jeder Zeile wird das erste Tabulatorzeichen, sofern davor lediglich null oder mehr Leerzeichen stehen, durch 8 Leerzeichen ersetzt, danach sorgt die Sprunganweisung b <Sprungzielname>
dafür, dass die Programmausführung wieder zur ersten Zeile zurückkehrt. Ist das letzte führende Tabulatorzeichen ersetzt, so matcht der Ausdruck /^<b>*<t>/
nicht mehr und der Block wird nicht ausgeführt, sodass das Programmende erreicht und die nächste Zeile eingelesen wird.
Hier wird die Ähnlichkeit mit Assembler-Sprachen deutlich, indem mit einer Bedingung und einem Label eine Kontrollstruktur vergleichbar dem in Hochsprachen üblichen repeat-until
aufgebaut wird.
Sonstige Anweisungen
Hold Space Manipulation
Eine mächtige (gleichwohl relativ unbekannte) Funktion von sed
ist der sogenannte Hold Space. Das ist ein frei verfügbarer Speicherbereich, der in seiner Arbeitsweise dem in manchen Assembler-Sprachen bekannten Akkumulator ähnelt. Direkte Manipulation der Daten im Hold Space ist zwar nicht möglich, aber Daten im Pattern Space können in den Hold Space verlagert, kopiert, oder auch mit dem Inhalt desselben vertauscht werden. Auch das Anhängen des Pattern Spaces an den Hold Space oder vice versa ist möglich.
Das folgende Beispiel verdeutlicht die Funktion des Hold Space: der Text einer „Kapitelüberschrift“ wird gespeichert und jeder Zeile des jeweiligen „Kapitels“ nachgestellt, die Zeile mit der Kapitelüberschrift selbst aber unterdrückt:
sed '/^=/ { s/^=// s/^/ (/ s/$/)/ h d } G; s/\n// ' inputfile | |
Input | Output |
=Kapitel1 Zeile 1 Zeile 2 Zeile 3 =Kapitel2 Zeile A Zeile B Zeile C |
Zeile 1 (Kapitel1) Zeile 2 (Kapitel1) Zeile 3 (Kapitel1) Zeile A (Kapitel2) Zeile B (Kapitel2) Zeile C (Kapitel2) |
Immer wenn eine Zeile mit „=“ beginnt, so wird der Anweisungsblock ausgeführt, der dieses Zeichen entfernt und dafür die restliche Zeile mit einem führenden Leerzeichen und Klammern versieht. Danach wird dieser Text in den Hold Space kopiert (h
) und aus dem Pattern Space gelöscht (d
), wodurch das Programm für diese Zeile beendet und die nächste Zeile gelesen wird. Da für „normale Zeilen“ die Bedingung des Eingangsblocks nicht zutrifft, wird lediglich die letzte Anweisung (G
) durchgeführt, die den Inhalt des Hold Space an den Pattern Space anhängt.
Mehrzeilen-Anweisungen
Nicht alle Textmanipulationen lassen sich innerhalb einzelner Zeilen ausführen. Manchmal müssen Informationen aus anderen Zeilen in die Entscheidungsfindung miteinbezogen werden, manchmal auch zeilenübergreifende Ersetzungen durchgeführt werden. Dafür sieht die sed
-Programmiersprache die Anweisungen N
, P
und D
vor, mit denen mehrere Zeilen des Eingabetexts gleichzeitig in den Pattern Space geladen (N
) und Teile davon ausgegeben (P
) oder gelöscht (D
) werden können. Ein typisches Anwendungsbeispiel ist der folgende Einzeiler (eigentlich zwei Einzeiler), der einen Text mit Zeilennummern versieht:
sed '=' inputfile | sed 'N; s/\n/<t>/' |
Der erste sed
-Aufruf druckt für jede Zeile im Eingangstext die Zeilennummer aus und danach die Zeile selbst. Der zweite sed
-Aufruf verbindet diese beiden Zeilen zu einer einzigen, indem erst die jeweils nachfolgende Zeile eingelesen (N
) und dann das automatisch eingefügte Zeilentrennzeichen („\n“) durch ein Tabulatorzeichen ersetzt wird.
Anwendungen, Optionen, Hinweise
Kapazitätsgrenzen
sed
unterliegt keinen (realen) Beschränkungen hinsichtlich der Dateigrößen. Abgesehen vom verfügbaren Plattenplatz, der eine praktische Grenze darstellt, realisieren die meisten Implementationen den Zeilenzähler als int
oder long int
. Bei den heute üblichen 64-Bit-Prozessoren kann die Gefahr eines Überlaufs deshalb vernachlässigt werden.
Wie die meisten textmanipulierenden Tools in UNIX unterliegt sed
allerdings einer Begrenzung hinsichtlich der Zeilenlänge (genauer: der Anzahl Bytes bis zum nachfolgenden newline
-Zeichen). Die Mindestgröße ist durch den POSIX-Standard festgelegt, die tatsächliche Größe kann von System zu System variieren und kann im jeweiligen Fall in der Kernel-Headerdatei /usr/include/limits.h
als Wert der Konstanten LINE_MAX
nachgeschlagen werden. Die Länge wird in Bytes angegeben, nicht in Zeichen (weshalb eine Umrechnung etwa bei der Verarbeitung von UTF-codierten Dateien, die einzelne Zeichen mit mehreren Bytes darstellen, nötig ist).
Greedyness
Vorlage:Siehe auch
Beim Geltungsbereich von RegExp
s wird zwischen greedy und non-greedy unterschieden. sed
-RegExp
s sind immer greedy, das bedeutet, dass die RegExp
immer den längstmöglichen Geltungsbereich hat:
/a.*B/; "'a', gefolgt von null oder mehr beliebigen Zeichen, gefolgt von 'B'" axyBBBskdjfhaaBBpweruBjdfh ; längstmöglicher Geltungsbereich (greedy) axyBBBskdjfhaaBBpweruBjdfh ; kürzestmöglicher Geltungsbereich (non-greedy) |
Der Grund ist, dass sed
auf Geschwindigkeit optimiert ist und non-greedy RegExp
s aufwendiges Backtracking erfordern würde. Will man ein Non-greedy-Verhalten erzwingen, so erreicht man dies üblicherweise durch negierte Zeichenklassen. Im obigen Beispiel etwa:
/a[^B]*B/ ; "'a', gefolgt von null oder mehr nicht-'B', gefolgt von 'B'" |
Praktische Grenzen in der Shell-Programmierung
Es sollte nicht unerwähnt bleiben, dass die allerhäufigste Anwendung von sed
(aber auch von awk
, tr
und ähnlichen Filterprogrammen) in der Praxis – die Manipulation ad hoc von Ausgaben anderer Kommandos, etwa so:
ls -l /path/to/myfile | sed 's/^\([^ ][^ ]*\) .*/\1/' # gibt Filetype und Filemode aus |
genaugenommen einen Missbrauch darstellt. Da jeder Aufruf eines externen Programmes die aufwendigen Systemaufrufe fork()
und exec()
erfordert, sind Shell-interne Methoden, etwa die sogenannte Variablenexpansion, selbst wenn sie deutlich länger zu schreiben sind, meist dem Aufruf von externen Programmen überlegen.[6] Die Faustregel dafür lautet: wenn die Ausgabe des Filterprozesses eine Datei bzw. ein Datenstrom ist, so ist das Filterprogramm zu verwenden, ansonsten ist Variablenexpansion vorzuziehen.
In-Place-Editing
Aufgrund der Art wie sed
Textmanipulationen durchführt, kann dies nicht direkt auf der Eingabedatei geschehen. Als Ausgabe wird eine von dieser getrennte Datei benötigt, die gegebenenfalls danach über die Eingangsdatei kopiert wird.
sed '…<Anweisungen>…' /path/to/inputfile > /path/to/output mv /path/to/output /path/to/input |
Dies ist auch so im POSIX-Standard vorgesehen. Die GNU-Version von sed bietet zusätzlich zum POSIX-Standard die Kommandozeilen-Option -i
. Diese erlaubt es, eine Datei scheinbar ohne Umweg (in place) zu verändern, tatsächlich wird aber im Hintergrund ebenfalls eine temporäre Datei angelegt. Diese wird im Fehlerfall nicht gelöscht und die Metadaten (Besitzer, Gruppe, Inode-Nummer, …) der Originaldatei auf jeden Fall verändert.
RegExp
-Notation
Es hat sich eingebürgert, regular Expressions – wie auch in den obigen Beispielen – durch Schrägstriche zu begrenzen. sed
erfordert dies allerdings nicht. Jedes Zeichen, das einem Ersetzungskommando folgt, wird als Begrenzer akzeptiert und dann in der Folge erwartet. Diese beiden Anweisungen sind deshalb gleichwertig:
s/^\([^ ][^ ]*\) \([^ ][^ ]*\)/\2 \1/ ; vertauscht erstes und zweites Wort einer Zeile s_^\([^ ][^ ]*\) \([^ ][^ ]*\)_\2 \1_ ; "_" statt "/" |
Dies ist praktisch, wenn der Schrägstrich als Teil der RegExp
benötigt wird, weil man sich dann das mühsame Escapen (Kenntlichmachen der Verwendung als Literal) ersparen kann. Man weicht dann einfach auf ein anderes, nicht verwendetes Zeichen aus.
Einige typische Verfahren
Löschung von Textteilen
Erfolgt durch Ersetzung durch nichts. Explizite Löschung für Teile einer Zeile ist nur vom Zeilenbeginn bis zum ersten Zeilentrennzeichen (D
) vorgesehen. Der Ausdruck
/Ausdruck/d |
löscht hingegen NICHT den Teil Ausdruck, sondern jede Zeile, die Ausdruck enthält! Ausdruck fungiert hier als die Adresse (siehe oben, 1-Adress-Variante des Kommandos d
).
Ansprechen von mindestens einem Zeichen
Im Umfang der POSIX-BREs ist – im Unterschied zu den GNU-BREs – der Quantor \+
für ein oder mehrere des vorangegangenen Ausdrucks nicht vorgesehen. Um portable sed
-Scripte zu schreiben, die nicht nur mit GNU-sed laufen, sollte der Quantor \{min,\}
verwendet werden.
/xa\+y/ ; GNU-Variante für "'x' gefolgt von einem oder mehr (aber nicht null) 'a', gefolgt von 'y'" /xa\{1,\}y/ ; dasselbe in POSIX: "'x' gefolgt von mindestens einem 'a', gefolgt von 'y'" |
Ersetzung mehrerer bzw. aller Vorkommen innerhalb einer Zeile
Ohne Angabe weiterer Optionen wird immer nur das erste Vorkommen eines Suchtexts der Ersetzungsregel unterworfen:
sed 's/alt/NEU/' inputfile | |
Input | Output |
alt alt alt alt alt alt alt alt alt alt alt alt alt alt alt |
NEU NEU alt NEU alt alt NEU alt alt alt NEU alt alt alt alt |
Dieses Verhalten kann allerdings durch die Angabe von Kommandoptionen geändert werden: Wird eine Zahl N angegeben, dann wird lediglich das N-te Vorkommen geändert, ein g
(für global) ändert alle Vorkommen:
sed 's/alt/NEU/g' inputfile | |
Input | Output |
alt alt alt alt alt alt alt alt alt alt alt alt alt alt alt |
NEU NEU NEU NEU NEU NEU NEU NEU NEU NEU NEU NEU NEU NEU NEU |
Filtern bestimmter Zeilen
Grundsätzlich gibt sed
immer den Inhalt des Pattern Spaces nach der letzten Anweisung aus. Will man dieses Verhalten für einzelne Zeilen unterdrücken, so kann man entweder über eine Regel bestimmte Zeilen löschen (explizite Filterung), aber es ist auch mit der Kommandozeilen-Option -n
möglich, dieses Verhalten insgesamt abzustellen (implizite Filterung). Ausgegeben wird dann nur noch, was mit dem ausdrücklichen Print
-Kommando (p
) angegeben wird. p
kann dabei entweder als eigene Anweisung oder als Option für andere Anweisungen dienen. Das Beispiel gibt aus dem bereits oben verwendeten Text nur noch die „Kapitelüberschriften“ aus:
sed -n 's/^=\(.*\)$/Kapitelüberschrift: \1/p' inputfile | |
Input | Output |
=Kapitel1 Zeile 1 Zeile 2 Zeile 3 =Kapitel2 Zeile A Zeile B Zeile C |
Kapitelüberschrift: Kapitel1 Kapitelüberschrift: Kapitel2 |
Debugging
Zur Fehlersuche kann es nützlich sein, sich Zwischenergebnisse ausgeben zu lassen um die Entwicklung im Pattern Space besser nachvollziehen zu können. Dazu kann die bereits oben erwähnte Option p
verwendet werden. Zeilen können durchaus mehrmals hintereinander ausgegeben werden. In dem obigen Beispielprogramm etwa:
sed '/^=/ { s/^=//p s/^/ (/p s/$/)/p h d } p G' inputfile |