Sed
Aus Gentoo Linux Wiki
Inhaltsverzeichnis
|
[Bearbeiten] Einleitung
Dieser Artikel will eine kurze Einführung in elementare Funktionen von "sed" geben, ohne Anspruch auf Vollständigkeit zu erheben. Er wendet sich v.a. an sed-Anfänger. Meine Ausführungen beruhen auf sed-4.1.4-r1. sed ist in der Standard Gentoo-Installation bereits vorhanden. Hilfreich für das Verständnis des Artikels sind grundlegende Kenntnisse von "Regular Expressions". Neben vielen anspruchsvollen sed-Tutorials, die den Anfänger oft überfordern, gibt es ein gut nachvollziehbares und klar strukturiertes Tutorial, das ich nur empfehlen kann: http://www.grymoire.com/Unix/Sed.html
[Bearbeiten] Installation
| Code: emerge von sed |
emerge -av sys-apps/sed |
[Bearbeiten] Erstellung einer Testdatei
| Code: Testdatei erstellen |
cat > sed-test.txt << EOF Dies ist Zeile 1 Hier isst ein Fehler Hier isst noch ein Frhler Dies ist die vorletzte Zeile Dies ist die letzte Zeile EOF |
[Bearbeiten] Grundlegendes zu sed
[Bearbeiten] Arbeitsweise von sed
sed arbeitet zeilenorientiert, d.h. es liest die Eingabedatei Zeile pro Zeile und wendet auf jede Zeile den bzw.die Befehle an. Eine Zeile endet dort, wo das Zeichen "linefeed", also 0x0A steht.
[Bearbeiten] Dateiübergabe an sed
Es gibt mehrere Möglichkeiten, die zu bearbeitende Datei an sed zu übergeben.
cat sed-test-txt | sed [anweisungen...] # Output von cat ist Input von sed sed [anweisungen...] < sed-test.txt # Umleitung von Stdin sed [anweisungen...] sed-test.txt # Dateiname als Kommandozeilenparamter
Die Ausgabe erfolgt nach Stdout, also auf den Bildschirm. Sie kann mittels Pipe-Symbol an andere Kommandos übergeben oder in eine Datei umgeleitet werden:
cat sed-test-txt | sed [anweisungen...] | less # Stdout ist Stdin für "less" cat sed-test-txt | sed [anweisungen...] > output.txt # Umleitung in Datei
[Bearbeiten] Aufbau der sed-Anweisungen
Die Anweisungen sind für Anfänger gewöhnungsbedürftig. Aber wenn man ein paar grundlegende Dinge verstanden hat, ist es nicht schwierig. Die sed-Anweisungen werden i.A. von einfachen Hochkommas umschlossen.
sed 'anweisung' < sed-test.txt
sed kann mehrere Anweisungen in einem Durchgang abarbeiten. In diesem Falle muss jedem Befehl die Option "-e" vorangestellt werden. Wenn nur eine Anweisung abgearbeitet werden soll, kann man "-e" weglassen.
sed -e 'anweisung1' -e 'anweisung2' < sed-test.txt
möglich, aber etwas unübersichtlicher ist auch diese Form:
sed -e 'anweisung1; anweisung2' < sed-test.txt
In vielen sed-Anweisungen kommt das Zeichen '/' vor. Es ist ein Begrenzungszeichen, also ein "delimiter". Eine Regular Expression, also ein Suchausdruck, wird durch zwei Schrägstriche begrenzt. Im folgende kürze ich "Regular Expression" durch "RE" ab.
sed '/isst/' < sed-test.txt # unvollständige Anweisung # sed durchsucht die Datei Zeile pro Zeile nach dem String "isst" # die RE "isst" wird durch "/" am Anfang und Ende eingegrenzt. # "/" ist also nichts anderes als ein Begrenzungszeichen, ähnlich wie Hochkommata
Die vorige Anweisung ist unvollständig, da sie nur aus der RE '/isst/' besteht. sed muss noch mitgeteilt werden, wie mit dem Suchergebnis zu verfahren ist. Hinter der durch die RE getroffenen Bereichsauswahl ist der Befehl anzugeben. Eine sed-Anweisung enthält also i.A. mindestens zwei Komponenten (die Bereichsauswahl kann in bestimmten Konstrukten fehlen):
a) Bereichsauswahl Auswahl der Zeilen, die bearbeitet werden sollen. Im vorigen Beispiel würde sich die Auswahl auf alle Zeilen beziehen, in denen "isst" vorkommt. b) Befehl Was soll mit den ausgewählten Zeilen passieren? Der einfachste Befehl lautet "p" (print), er gibt die selektierten Zeilen am Bildschirm aus.
Das vorige Beispiel wird vervollständigt:
sed '/isst/p' < sed-test.txt # Durchsuchen der Datei nach Zeilen, die "isst" enthalten # Die selektierten Zeilen werden am Bildschirm ausgegeben # "/" umschließt die RE, hat also mit dem Befehl "p" nichts zu tun.
Leider liefert die sed-Anweisung ein unbefriedigendes Ergebnis, da alle Zeilen ausgegeben werden. Dies liegt an der Arbeitsweise von sed, auf die ich hier nicht näher eingehen will. Um das gewünschte Ergebnis zu erhalten, muss sed mit der Option "-n" aufgerufen werden:
sed -n '/isst/p' < sed-test.txt # beim p-Befehl immer die Option "-n" verwenden! # aufgrund der Option "-n" werden nur die Zeilen ausgegeben, in denen "isst" vorkommt.
Wenn man die Option "-e" verwendet (s.o.), muss beachtet weden, dass "-n" vor "-e" steht.
sed -n -e '/isst/p' < sed-test.txt # "-e" (s.o.) muss hinter "-n" stehen!
[Bearbeiten] explizite Bereichsauswahl
Im vorigen Kapitel wurde beschrieben, wie Zeilen aufgrund einer RE selektiert werden können. Es ist jedoch auch möglich, bestimmte Zeilen explizit auszuwählen, also z.B. "Zeile 1", "Zeile 1-2", "letzte Zeile" usw. In diesem Falle wird die Bereichsauswahl direkt angegeben, sie steht also nicht zwischen "/" wie bei RE-Ausdrücken. Der Befehl 'p' steht direkt hinter der Bereichsauswahl.
sed -n '1p' < sed-test.txt # nur die erste Zeile ausgeben; Option "-n" nicht vergessen! sed -n '$p' < sed-test.txt # nur die letzte Zeile ausgeben
Wenn mehrere Zeilen selektiert werden sollen, werden Bereichsanfang und -ende durch ein Komma getrennt.
sed -n '1,2p' < sed-test.txt # Zeile 1-2 ausgeben
[Bearbeiten] Suchen und Ersetzen
Dies ist der häufigste Verwendungszweck von sed. Leider ist die Syntax zunächst etwas schwer zu durchschauen, aber Übung macht den Meister. In diesem Falle steht das Trennzeichen "/" an drei Stellen: vor und hinter der RE, also dem "Suchstring", und hinter dem Ersetzungsstring. Der mittlere Schrägstrich ist also Ende-Delimiter für die RE und gleichzeitig Anfangs-Delimiter für den Ersetzungsstring. Logischer Weise müsste der Schrägstrich vier mal vorkommen, aber sed hat die beiden mittleren Schrägstriche zu einem verschmolzen.
Beispiel: "isst" soll in allen Zeilen durch "ist" ersetzt werden
eigentlich müssten pro String ein Anfangs-/Ende-Delimiter verwendet werden. /isst//ist/ # beide Strings müssten durch "/" begrenzt werden sed verschmilzt die mittleren Schrägstriche zu einem! /isst/ist/ # dies erscheint zunächst unlogisch.
Der Befehl "s" für "suchen & ersetzen" steht vor(!) der RE.
sed 's/isst/ist/' < sed-test.txt # Befehl "s" steht am Anfang! # sed selektiert alle Zeilen, in denen "isst" vorkommt, ersetzt "isst" durch "ist" # und gibt das Ergebnis am Bildschirm aus
Um die Änderungen in die Datei zu schreiben, gibt es zwei Möglichkeiten.
a) mit Hilfe einer temporären Datei sed 's/isst/ist/' < sed-test.txt > sed-test2.txt # Ausgabe in temporäre Datei umleiten vim sed-test2.txt # Ergebnis kontrollieren! mv sed-test2.txt sed-test.txt # Originaldatei ersetzen durch temporäre Datei
b) direkt sed -i 's/isst/ist/' sed-test.txt # Vorsicht, Änderungen werden direkt in Datei geschrieben!
Im Beispiel würde sed in jeder Zeile nur das erste gefundene "isst" durch "ist" ersetzen. Um zu erreichen, dass alle Fundstellen geändert werden, muss ans Ende der Anweisung der g-Spezifizierer (=global) gesetzt werden:
sed 's/isst/ist/g' < sed-test.txt
[Bearbeiten] selektives Suchen und Ersetzen
Beim "normalen" Suchen und Ersetzen werden alle Zeilen in den Suchvorgang einbezogen. Es kommt jedoch immer wieder vor, dass die Ersetzung nur innerhalb eines bestimmten Bereiches erfolgen soll. Die Bereichsauswahl kann, wie oben beschrieben, über eine RE oder explizit erfolgen.
Beispiel: Ans Ende aller Zeilen, in denen "isst" vorkommt, soll " --Fehler!" angehängt werden. Das normale Suchen und Ersetzen hilft hierbei nicht weiter, denn "isst" soll ja nicht ersetzt werden, sondern dient nur Selektion der Zeilen, an die das Suffix angehängt werden soll. Es sind also zwei Vorgänge erforderlich: (1) Bereichsauswahl über die RE "/isst/" (2) Suchen des Zeilenendes und ersetzen durch " --Fehler!". Genau genommen wird nicht das Zeilenende ersetzt, sondern ein gedachte fiktives "leer-Zeichen" (sozusagen "") vor dem Zeilenende. Damit sed weiß, dass sich Suchen und Ersetzen nur auf die Bereichsauswahl bezieht, steht es hinter der Bereichsauswahl in geschweiften Klammern. Sie können auch weggelassen werden. Ich rate jedoch dazu, sie zu verwenden, da die Logik des Befehls dadurch klarer wird.
sed '/isst/{s/$/ --Fehler!/}' < sed-test.txt
# /isst/ -> Bereichsauswahl über RE
# {...} -> Anweisung in "{...}" bezieht sich auf die vorangestellte Bereichsauswahl
# $ -> Platzhalterzeichen für "Zeilenende" (in Suchen & Ersetzen Anweisungen)
# s/$/ --Fehler!/ -> fiktives "leer-Zeichen" vor dem Zeilenende wird ersetzt durch " --Fehler!"
Bei expliziter Bereichsauswahl funktioniert dies analog. Beispiel: Ans Ende der 2. und 3. Zeile soll " --Fehler!" gesetzt werden.
sed '2,3{s/$/ --Fehler!/}' < sed-test.txt
# 2,3 -> explizite Bereichsauswahl: Zeile 2-3
Beachten Sie, dass die explizite Bereichsauswahl durch keine Delimiterzeichen "/" eingegrenzt ist. Beispiel: Ans Ende der letzten Zeile soll " --Ende" gesetzt werden.
sed '${s/$/ --Fehler!/}' < sed-test.txt
# $ -> explizite Bereichsauswahl; "$" steht hier für "letzte Zeile"
Beispiel: an den Anfang der ersten Zeile soll "Anfang--" gesetzt werden.
sed '1{s/^/Anfang--/}' < sed-test.txt
# 1 -> explizite Bereichsauswahl
# ^ -> Platzhalterzeichen für "Zeilenanfang"
# ein fiktives "leer-Zeichen" ("") am Zeilenanfang wird ersetzt durch "Anfang--"
Der vorige Befehl funktioniert auch ohne {}, ist aber schwerer zu lesen:
sed '1s/^/Anfang--/' < sed-test.txt
[Bearbeiten] delete-Befehl
Der delete-Befehl ist die Umkehrung des print-Befehls: Die bei der Bereichsauswahl (mittels RE oder explizit) selektierten Zeilen werden "gelöscht", also nicht ausgegeben.
sed '/isst/d' < sed-test.txt # Zeilen, die "isst" enthalten, werden nicht ausgegeben sed '3d' < sed-test.txt # die dritte Zeile wird nicht ausgegeben
[Bearbeiten] besondere Zeichen
[Bearbeiten] Negation
Das Zeichen "!" hinter der Bereichsauswahl bewirkt eine Negation.
sed '/isst/!d' < sed-test.txt # Zeilen, die "isst" nicht enthalten, werden nicht ausgegeben sed -n '1!p' < sed-test.txt # alle Zeilen außer Zeile 1 werden ausgegeben
[Bearbeiten] $-Zeichen: letztes Element
Das $-Zeichen steht bei der Bereichsauswahl für "letzte Zeile":
sed -n '$p' < sed-test.txt # letzte Zeile ausgeben
In einer RE steht das $-Zeichen für "Zeilenende":
sed -n '/Zeile$/p' < sed-test.txt # Zeilen ausgeben, in denen "Zeile" am Ende steht
Möchte man dem Zeilenende eine Zeichenkette anhängen, genügt ein $ als Suchmuster:
sed -n 's/$/ und Schluss!/p' < sed-test.txt # Jede Zeile endet mit ' und Schluss!'
[Bearbeiten] ^-Zeichen
Das ^-Zeichen steht in einer RE für Zeilenanfang:
sed -n '/^Hier/p' < sed-test.txt # Zeilen ausgeben, in denen "Hier" am Anfang steht
Innerhalb einer character class in einer RE steht es für Verneinung.
sed -n '/F[^e]hler/p' < sed-test.txt # Zeilen ausgeben, in denen das Wort "F.hler" vorkommt, wobei der 2. Buchstabe kein "e" ist
Möchte man dem Zeilenanfang eine Zeichenkette voranstellen genügt ein ^ als Suchmuster:
sed -n 's/^/Am Anfang stand /p' < sed-test.txt # Jede Zeile beginnt mit 'Am Anfang stand '
[Bearbeiten] Control-Character
Mit Hilfe von sed können verschiedenste Konvertierungen durchgeführt werden, z.B. von DOS- (CR-LF) zu Unix Dateiendezeichen (LF). Control-Character können entweder als escape-character oder als hexadezimal-character dargestellt werden.
a) als escape-character a="Zeile1\r\nZeile2" # am Ende von Zeile1 steht CR-LF (=DOS-Zeilenende) echo -e "$a" | cat -A # Inhalt wird inkl. Kontrollzeichen ausgegeben und darstellbar gemacht (cat -A) # der carriage-return wird als ^M dargestellt (0x0D) echo -e "$a" | sed 's/\r//' | cat -A # der carriage-return wird durch "leer" ersetzt, ^M wird nicht mehr angezeigt unset a
b) als hexadezimal-character a=$'Zeile1\x0d\x0aZeile2' # am Ende von Zeile1 steht CR-LF (0x0d+0x0a) echo -e "$a" | cat -A echo -e "$a" | sed 's/\x0d//' | cat -A unset a
wichtige control-character Bezeichnung escape hex "cat -A" linefeed \n \x0a $ carriage-return \r \x0d ^M tab \t \x09 ^I
Kontrollzeichen innerhalb von Variablen lassen sich auch sehr gut mit Hilfe des l-Befehls von sed am Bildschirm darstellen. Dies ist eine Alternative zu "cat -A"
a=$'Tabulator: \x09 CR: \x0d LF: \x0a' echo -e "$a" | sed -n 'l'
[Bearbeiten] alternatives delimiter-Zeichen
Das delimiter-Zeichen "/" kann durch ein beliebiges anderes Zeichen ersetzt werden. Dies ist dann sinnvoll, wenn "/" innerhalb einer RE vorkommt, also z.B. bei einer Pfadangabe.
cat /etc/apache2/httpd.conf | sed 's@/var/www@/srv/www@' # "@" als delimiter-Zeichen
[Bearbeiten] Dateien ergänzen
Bisher ging es nur darum, den Inhalt einer Datei in selektierter bzw. geänderter Form ("suchen & ersetzen") auszugeben. Es ist jedoch auch möglich, eine Datei zu erweitern.
[Bearbeiten] Zeile am Dateianfang einfügen
Dies ist auch ohne sed möglich, z.B. mit Hilfe von echo und cat
cat - <<< "Anfang" sed-test.txt > sed-test2.txt echo "Anfang" | cat - sed-test.txt > sed-test2.txt echo "Anfang" > sed-test2.txt ; cat sed-test.txt >> sed-test2.txt
Mit Hilfe von sed geht dies eleganter.
sed '1iAnfang' < sed-test.txt > sed-test2.txt # 1=Zeile 1 (explizite Bereichsauswahl) # i=insert-Befehl
[Bearbeiten] Zeile innerhalb des Textes einfügen
Die folgende Anweisung fügt eine neue Zeile vor der vorletzten Zeile ein.
sed '/vorletzte/idies ist die vorvorletzte Zeile' < sed-test.txt > sed-test2.txt # /vorletzte/ = Bereichsauswahl über RE # i=insert-Befehl
[Bearbeiten] Zeile ans Dateiende anhängen
sed '$aEnde' < sed-test.txt > sed-test2.txt # $=letzte Zeile (explizite Bereichsauswahl) # a=append-Befehl
[Bearbeiten] Zeichen am Zeilenanfang einfügen
Das Zeichen "#" soll in allen Zeilen an den Zeilenanfang gesetzt werden.
sed 's/^/#/' < sed-test.txt # s =suchen & ersetzen # /^/ ="Zeilenanfangszeichen" # /#/ = Replace-String
Die Anweisung erscheint auf den ersten Blick nicht ganz logisch. Sie ist folgendermaßen zu interpretieren: Ersetze in jeder Zeile das am Zeilenanfang gedachte fiktive "leer-Zeichen" (sozusagen "") durch das #-Zeichen. Es wird also nicht der Zeilenanfang selbst ersetzt, was ja gar nicht möglich wäre, da es kein Zeilenanfangszeichen analog zum Zeilenendezeichen gibt. Einfacher verständlich wird die Anweisung, wenn man ein bestimmtes Wort am Zeilenanfang ersetzen lässt:
sed 's/^Hier/Dort/' < sed-test.txt # "Hier" am Zeilenanfang wird ersetzt durch "Dort"
Beispiel: Das Zeichen "#" soll bei Zeilen, die mit "Hier" beginnen an den Zeilenanfang gesetzt werden.
sed 's/^Hier/#Hier/' < sed-test.txt
Beispiel: Das Zeichen "#" soll in Zeilen, in denen "letzt" vorkommt, an den Zeilenanfang gesetzt werden. Dieser Fall unterscheidet sich von den vorigen, weil das "suchen & ersetzen" auf einen bestimmten Bereich beschränkt werden muss. Hierzu werden geschweifte Klammern verwendet.
sed '/letzt/{s/^/#/}' < sed-test.txt
# /letzt/ =RE; Zeilen, in denen "letzt" vorkommt, werden selektiert
# {s...} = "suchen & ersetzen" wird nur in den selektierten Zeilen ausgeführt
[Bearbeiten] Verkettung mehrerer sed-Anweisungen
sed-Anweisungen lassen sich entweder mit Hilfe der Option "-e" aneinanderreihen oder mittels Pipe. Die Aneinanderreihung mittels Pipe macht v.a. dann Sinn, wenn die Auswahlmenge schrittweise reduziert werden soll, analog zum Aneinanderpipen von grep-Kommandos.
Beispiel: In der Datei httpd.conf sollen nur die Zeilen ausgegeben werden, die nicht mit "#" beginnen. Sodann soll "Allow" durch "Deny" ersetzt werden. Zunächst werden Zeilen selektiert, die nicht mit "#" beginnen. Die Ausgabe wird sodann mittels Pipe an einen weiteren sed-Prozess zu übergeben, der die Wortersetzung durchführt.
cat httpd.conf | sed -n '/^[^#]/p' | sed 's/Allow/Deny/' # (1) Reduktion der Auswahlmenge # (2) Ersetzung
Eine andere Möglichkeit besteht darin, mehrere Anweisungen an sed zu übergeben, die sukzessive abgearbeitet werden. Dies kann z.B. dann verwendet werden, wenn innerhalb einer Datei mehrere "Suchen & Ersetzen"-Befehle nacheinander durchgeführt werden sollen. Nach jedem s-Befehl werden sämtliche Zeilen ausgegeben, sodass beim folgenden s-Befehl wieder die gesamte Datei zur Verfügung steht.
Beispiel: in der Datei httpd.conf soll "Allow" durch "Deny" ersetzt werden und "localhost" durch "myserver"
cat httpd.conf | sed -e 's/Allow/Deny/g' -e 's/localhost/myserver/g' (1) Ersetzung1 (2) Ersetzung2
Statt mehrere Anweisung mittels "-e" aneinanderzureihen, kann man diese auch durch ";" verketten, wobei das einfache Hochkomma nur am Anfang und am Ende stehen darf. Die Option "-e" kann hierbei weggelassen werden.
cat httpd.conf | sed 's/Allow/Deny/g; s/localhost/myserver/g' # analog zur vorigen Anweisung
[Bearbeiten] Regular Expressions
Eine Regular Expression (im folgenden mit RE abgekürzt) ist so etwas wie ein Suchmuster, das dazu dient, komplizierte Suchanfragen mit wenigen Zeichen zu formulieren. Die Syntax ist standardisiert und wird von verschiedenen Programmen verstanden, z.B. sed, egrep, expr, perl. Das interpretierende Programm versucht, aus einer größeren Zeichenmenge (i.A. einer Zeile) eine Teilmenge zu finden, die zu der RE "passt". Oft gibt es mehrere Teilmengen, die passen. In diesem Fall wird die größte Teilmenge als Ergebnis verwendet.
[Bearbeiten] Platzhalterzeichen
Ein wichtiges Element der REs ist das Platzhalterzeichen ".". Es ist Platzhalter für jedes beliebige Zeichen, also numerische, alphabetische oder Sonderzeichen. Ein "." steht für ein einziges beliebiges Zeichen.
echo "ihr Name ist Mayer, sein Name ist Meier" | sed -n '/Ma.er/p' # die RE "Ma.er" passt auf "Mayer", daher ist die Suchbedingung erfüllt. # Nicht die Ergebnismenge "Mayer", sondern die ganz Zeile wird ausgeben.
echo "ihr Name ist Mayer, sein Name ist Meier" | sed -n '/M..er/p' # "Mayer" und "Meier" passen, es gibt also 2 Ergebnismengen. # Wie im ersten Fall wird die ganze Zeile ausgegeben. # Die Anzahl der Ergebnismengen erfährt man nicht.
Die Anzahl der Platzhalterzeichen lässt sich weiter spezifizieren. Leider müssen die geschweiften Klammern, "{" und "}" escaped werden durch Voranstellung des "\".
.\{n\} # n mal "."
.\{,n\} # 0-n mal "."
.\{n,\} # n bis unendlich mal "."
.\{m,n\} # m-n mal "."
.* # 0 bis unendlich mal "."
.\+ # 1 bis unendlich mal "."
.\? # 0-1 mal "."
echo "ihr Name ist Mayer, sein Name ist Meier" | sed -n '/M.\{2\}er/p'
# die RE "M..er" ist identisch mit "M.\{2\}er"
# die Klammern müssen "escaped" werden, damit sed sie richtig interpretiert: \{...\}
echo "ihr Name ist Mayer, sein Name ist Meier" | sed -n '/M.*er/p' # Es gibt mehrere Ergebnismengen: # (1) Mayer # (2) Mayer, sein Name ist Meier # (3) Meier # Es gilt (2), weil es die größte Ergebnismenge ist
Unabhängig davon, ob keine, eine oder mehrere Ergebnismengen gefunden werden, wird immer die gesamte Zeile ausgegeben. Wenn man nur die effektive (größte) Ergebnismenge sehen will, muss man egrep mit der "-o" Option zu Hilfe nehmen
echo "ihr Name ist Mayer, sein Name ist Meier" | egrep -o 'M.*er' # Die RE steht anders als bei sed innerhalb von einfachen Hochkommta. # -o bewirkt, dass nur die effektive Ergebnismenge angezeigt wird
Die effektive Ergebnismenge lässt sich auch durch den s-Befehl herausbekommen:
echo "ihr Name ist Mayer, sein Name ist Meier" | sed 's/M.*er/X/g' # Beim Suchen & Ersetzen wird die effektive Ergebnismenge durch "X" ersetzt
[Bearbeiten] die einfachste RE
Die einfachste RE besteht aus einem Suchstring ohne weitere Deskriptoren, also z.B. dem Wort "Mayer".
echo "ihr Name ist Mayer, sein Name ist Meier" | sed 's/Mayer/X/g' # "Mayer" ist effektive Ergebnismenge und wird durch "X" ersetzt
Beachten Sie, dass die folgende RE ein anderes Ergebnis liefert:
echo "ihr Name ist Mayer, sein Name ist Meier" | sed 's/.*Mayer.*/X/g' # diese RE trifft auf die gesamte Zeile zu, nicht nur auf "Mayer"!
[Bearbeiten] REs beim "Suchen & Ersetzen"
Beim reinen s-Befehl werden immer alle Zeilen ausgegeben, unabhängig davon, ob Ersetzungen durchgeführt wurden oder nicht. Dies lässt sich ändern, indem man ihn mit dem p-Befehl kombiniert. Die Abläufe sind wie folgt:
a) p-Befehl echo "dies wird nicht ausgegeben" | sed -n '/.*diiss.*/p' # (1) gibt es eine Ergebnismenge, die zur RE "passt"? # (2) wenn ja, gibt die ganze Zeile aus. b) s-Befehl ohne p-Befehl echo "dies wird ausgegeben" | sed 's/wird/wird immer/' # (1) gibt es eine Ergebnismenge, die zur RE "passt"? # (2) wenn ja, welches ist die effektive Ergebnismenge? # (3) ersetze diese durch den Replace-String # (4) gib die ganze Zeile aus (egal, ob geändert oder nicht) c) s-Befehl mit p-Befehl echo -e "xxx\nnächste Zeile" | sed -n 's/xxx/Ausgabe/p' # Die Kombination von s- und p-Befehl bewirkt, dass # nur die geänderten Zeilen ausgegeben werden. # Die Option -n darf nicht vergessen werden.
Im Allgemeinen ist es sinnvoll, den g-Spezifizierer ("global") zu verwenden, der direkt vor oder hinter dem p-Befehl steht:
echo -e "xxx--xxx\nnächste Zeile" | sed -n 's/xxx/Ausgabe/pg' # alle "xxx" werden ersetzt
[Bearbeiten] Character Classes
Eine Character Class definiert einen Zeichenvorrat. Sie besteht aus einer Menge von ein oder mehreren Zeichen und wird innerhalb von eckigen Klammern angegeben. Die Reihenfolge der Zeichen ist beliebig.
Beispiele:
[ae] -> Zeichenvorrat besteht nur aus "a" und "e" [aeiou] -> alle Vokale in Kleinbuchstaben [aeiouAEIOU] -> ...in Klein- und Großbuchstaben [a-fh-k] -> Buchstaben a-f und h-k [^a] -> alle Zeichen außer "a" [^abc] -> alle Zeichen außer "a", "b" oder "c" (Reihenfolge egal!)
Es gibt bereits vordefinierte Klassen (POSIX-classes), die zwecks einfacherer Darstellung verwendet werden können:
POSIX classes [:upper:] [A-Z] Großbuchstaben [:lower:] [a-z] Kleinbuchstaben [:alpha:] [A-Za-z] beides [:alnum:] [A-Za-z0-9] Ziffern und Groß-/Kleinbuchstaben [:digit:] [0-9] Ziffern [:xdigit:] [0-9A-Fa-f] hexadezimal Ziffern [:punct:] [.,!?:.] Punkt-Zeichen [:blank:] [ \t] "Whitespace" (Blank oder Space) [:space:] [ \t\n\r\f\v] Blank [:cntrl:] Kontrollzeichen [:graph:] [^ \t\n\r\f\v] Druckbare Zeichen ohne Space [:print:] [^\t\n\r\f\v] Druckbare Zeichen mit Space
Beispiel: Die Buchstaben "y" und "i" sollen durch "xx" ersetzt werden.
echo -e "Mayer\nMaier\nMeyer\nMeier" | sed 's/[yi]/xx/'
Beachten Sie, dass beim Suchen und Ersetzen immer alle Zeilen komplett ausgegeben werden, unabhängig davon ob Ersetzungen durchgeführt wurden oder nicht. Daher lassen sich mehrere Suchen und Ersetzen Anweisungen gut aneinanderreihen.
Beispiel: "i" und "y" sollen durch "ii" ersetzt werden, sodann "a" und "e" durch "oo"
echo -e "Mayer\nMaier\nMeyer\nMeier" | sed -e 's/[iy]/ii/g' -e 's/[ae]/oo/g' # "Mooiioor"
[Bearbeiten] Bezugnahme auf die Ergebnismenge
Bei den bisherigen Anweisungen wurde der gesamte Zeileninhalt ausgegeben. Es gibt jedoch die Möglichkeit, direkt auf die effektive Ergebnismenge oder einen Teil daraus Bezug zu nehmen. Die hierfür erforderliche s-Anweisung führt kein Suchen und Ersetzen durch, sondern durchsucht den Datenstrom anhand der RE und bildet aufgrund des Ausdrucks in () nochmals eine Teilmenge, die ausgegeben werden kann.
Beispiel: Aus der folgenden Zeile soll nur das Element "blau" ausgegeben werden.
echo "-rot-blau-gelb" | sed 's/.*\(blau\).*/\1/g'
Die gesamte RE lautet: /.*blau.*/
Es soll jedoch nur der Teil der Ergebnismenge ausgegeben werden, der "blau" enthält. Folglich ist "blau" in runde Klammern zu setzen.
/.*\(blau\).*/
echo "-rot-blau-gelb" | sed 's/.*\(blau\).*/\1/g' # s -> "Suchen und Ersetzen", wobei kein "Ersetzen" durchgeführt wird # .* -> Beginn der RE: beliebiges Zeichen 0-n mal # \(blau\) -> "blau" soll, falls es sich aus der Ergebnismenge extrahieren läßt, ausgegeben werden # .* -> Ende der RE: beliebiges Zeichen 0-n mal # \1 -> die Ergebnismenge der 1. geklammerten RE wird ausgegeben # bei mehreren REs, wird jeder RE eine Zahl zugeordnet: \1 = 1.RE \2 = 2.RE usw.
[Bearbeiten] oder-Verknüpfung mehrerer REs
echo "--rot--gelb--" | sed 's/\(gelb\|rot\)/x/g' echo "--rot--gelb--" | sed 's/gelb\|rot/x/g' # die runden Klammern und das oder-Zeichen "|" müssen escaped werden # die runden Klammern können weggelassen werden
SK, Ostfildern, den 15.7.2006
