Wie ich im ersten Teil bereits beschrieben habe, arbeitet man bei sed meist mit Mustern, die bestimmte Teile definieren, an denen Änderungen vorgenommen werden sollen. Diese Muster werden typischerweise durch reguläre Ausdrücke definiert. GNU Sed unterstützt dabei einfache reguläre Ausdrücke (basic regular expressions) bzw. mit dem Schalter -r erweiterte reguläre Ausdrücke (extended regular expressions, z.B. was egrep benutzt). Die wichtigsten Regeln mal kurz erklärt:
^ Anfang der Zeile
$ Ende der Zeile
. irgendein Zeichen
[...] irgendein Zeichen welches innerhalb der eckigen Klammer angegeben ist.
[a-z] passt auf alle Kleinbuchstaben des Alphabets, [abc] entweder auf
a oder auf b oder auf c
[^...] irgendein Zeichen welches nicht innerhalb der eckigen Klammer angegeben
ist. [^a-z] passt auf alle Zeichen, die keine Kleinbuchstaben sind.
\(\) Klammerung (bei erweiterten regulären Ausdrücken ohne Backslash, also ().
Damit kann man sich Ausdrücke merken und später auf sie referenzieren
mit den Variablen \1 bis \10 D.h. man kann auf maximal 10 verschiedene
Zeichenketten referenzieren, mehr geht nicht. Dabei zählt immer die
Reihenfolge der öffnenden Klammer.
(\(a\)b\1 passt auf "aba" aber nicht auf "abba")
* Ein sogenannter Quantifier. Der vorherige Ausdruck kann 0 bis beliebig
oft auftreten, dabei wird ein längerer passender Ausdruck bevorzugt
(sogenannte Greediness, "Gier").
a* passt also auf "", a, aa, aaa, und so weiter, bei einer Zeichenkette
von baah, wird es aber immer auf aa passen.
\? Der vorherige Ausdruck passt genau 0 der 1 Mal (erweiterte reguläre
Ausdrücke: ?)
\+ passt auf den vorherigen Ausdruck 1 mal oder mehr. Längere Treffer
werden dabei bevorzugt. (Bei erweiterten regulären Ausdrücken +).
\{n\} passt auf den vorherigen Ausdruck genau n-Mal. (erweiterte reguläre
Ausdrücke: {n})
\{n,\} passt auf den vorherigen Ausdruck n oder mehrmals. (erweiterte reguläre
Ausdrücke: {n,})
\{n,m\} passt auf den vorherigen Ausdruck n oder m Mal (m>n). Längere Treffer
werden bevorzugt. (erweiterte reguläre Ausdrücke: {n,m})
\{,m\} passt auf den vorherigen Ausdruck maximal m Mal. Längere Treffer werden
bevorzugt. (erweiterte reguläre Ausdrücke: {,m}).
\| Entweder der vorherige Ausdruck oder der nachfolgende Ausdruck.
(bei erweiterten regulären Ausdrücken |). a\|b passt auf a oder auf b.
\n passt auf einen Zeilenumbruch¹ (siehe auch Anmerkung).
\$ passt auf ein $-Zeichen (denn $ steht ja für das Ende der Zeile).
So kann man mit dem Backslash nachstehende Zeichen maskieren. \* passt
auf einen *, und so weiter...
Das ist soweit das Wichtigste zu den regulären Ausdrücken, die von Sed unterstüzt werden. Die Syntax ist nicht so schwer, man muß sich nur merken, welche der vielen Dialekte der regulären Ausdrücke sed beherrscht. Des weiteren unterstüzt sed Posix Character Classes, d.h. das Muster [[:lower:]] passt auf alle Kleinbuchstaben und [[:upper:]] auf alle Großbuchstaben. Außerdem beherrscht sed noch Patterns, die mit einem Backslash anfangen. '\w' passt zum Beispiel auf ein "Word"-Character, als Character-Klasse ausgedrückt: [A-Za-z0-9_], '\W' passt auf ein Non-Word-Character und somit der Umdrehung von '\w' (also [^a-zA-Z0-9_].). Weitere \-Muster werden unterstüzt, ich verweise dafür mal auf das Manual bzw. die Info-Seiten.
¹Des weiteren sollte man beachten, dass sed seinen Input zeilenorientiert liest. D.h. normalerweise wird ein Pattern mit einem '\n' für einen Zeilenumbruch nicht passen. Man kann darum arbeiten, aber man sollte das im Hinterkopf behalten, wenn man '\n' benutzt.
So fortgeschrittene Sachen wie "look-ahead", "look-behind", non-greedy Matches oder non-capturing Groups der Perl-kompatiblen regulären Ausdrücke beherrscht sed leider nicht. (Eine gute Übersicht über die vorhandenen Möglichkeiten gibt die Wikipedia.)
Diese Ausdrücke definieren nun, wo etwas gemacht wird. Wenn man die Zeilennummer weiß, kann man aber auch diese benutzen. Zusätzlich erlaubt es GNU Sed noch Schrittfolgen zu definieren. Also zum Beispiel von Zeile 1 ausgehend, jede zweite (also 1,3,5,7...). 1~2 passt genau auf dieses Muster, während 0~2 jede gerade Zeile ausgibt. Verallgemeinert gesagt x~y, wobei x die Startzeile angibt und y die Schrittfolge. $ als Zeilennummer passt dagegen immer auf die letzte Zeile.
Nun haben wir ganz kurz die Adressmöglichkeiten besprochen. Nun möchte man normalerweise auch was machen. Dafür versteht sed sogenannte Kommandos. Diese Kommandos verstehen normalerweise alle 2-Adressen (nämlich eine Start- und eine Ende-Adresse), eine Adresse
Nun haben wir ganz kurz die Adressmöglichkeiten besprochen. Nun möchte man normalerweise auch was machen. Dafür versteht sed sogenannte Kommandos. Diese Kommandos verstehen entweder keine Adressen (z.B. Sprungangaben und Kommentare), Eine oder keine Adressangaben oder 2 Adressen (nämlich Start- und Ende-Adresse).
Die folgenden Kommandos kennt dabei Gnu Sed:
'#' Kennzeichnet einen Kommentar
's/x/y/' ersetze x durch y
':' definiert eine Sprungmarke
'=' Gib die Zeilennummer aus.
'a\
Text' Hängt "Text" als neue Zeile an
'i\
Text' Fügt "Text" in die aktuelle Zeile ein
'c\
Text' Ersetze die aktuelle Zeile durch "Text"
q Beende
r foobar hängt den Inhalt der Datei foobar hinter die aktuelle Zeile ein.
R foobar Fügt eine Zeile der Datei foobar hinter die aktuelle Zeile ein.
b foobar springe zur Sprungmarke foobar
'p' Print (ausgeben des Treffers)
t foobar springe zur Sprungmarke foobar, wenn vorher das letzte s-Kommando
erfolgreich war
t foobar springe zur Sprungmarke foobar, wenn vorher das letzte s-Kommando
nicht erfolgreich war
d lösche aktuell eingelesene Zeile und fang von vorne an
n lese nächste Zeile ein
N lese nächste Zeile ein und hänge an die aktuelle Zeile an.
w foobar Schreibe aktuellen Inhalt in Datei foobar
W foobar Schreibe nur erste Zeile des aktuellen Inputs in Datei (später mehr dazu).
'y/a/b/' Ersetze a durch b (funktioniert so ähnlich wie tr).
Mit y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ werden alle
Kleinbuchstaben durch die Großbuchstaben ersetzt.
! Führe nachfolgendes Kommando für alle Zeilen außer der aktuellen aus.
(Negation)
Normalerweise liest Sed den Input (bis zum Zeilenende \n) in den sogenannten "Pattern Space" und führt die Kommandos im Pattern Space aus und gibt anschließend die Änderungen aus. Daneben gibt es noch einen extra Puffer, den sogenannten Hold Space. In ihm kann man Daten zwischenspeichern und später wieder verwenden. Dafür gibt es die folgenden Kommandos.
D Lösche bis zum ersten Zeilenumbruch im Pattern Space und fang von vorne
an
x Tausche die Inhalte des Pattern Spaces und Hold Spaces aus.
h Kopiere Pattern Space in den Hold Space Puffer
H Hänge Pattern Space an den Hold Space Puffer an
g Kopiere Hold Space Puffer in den Pattern Space
g Hänge Hold Space Puffer an den Pattern Space an
Der Aufbau eines Kommandos ist dabei folgender:
sed -n '3p' gibt Zeile 3 und nur Zeile 3 aus. sed -n '3,5p' gibt alles von Zeile 3 bis 5 aus (inklusive). (Normalerweise wird bei Sed der Input immer auch ausgegeben, mit -n unterdrückt man das und nur wenn man es explizit verlangt (Kommando p) wird etwas ausgegeben).
Das einfachste Kommando ist das '#' Allein für sich macht das gar nichts und gibt einfach alles aus:
sed '#' foobar
Der Fuchs ist rot und rot sind auch Äpfel.
Das gleiche erreicht man auch mit dem "Kommando" ; welches eigentlich verschiedene Kommandos trennt. In diesem Fall führt es ein "Null" Kommando aus und gibt daher alles aus, was es einliest:
sed ';' foobar
Der Fuchs ist rot und rot sind auch Äpfel.
Das verbreiteste Kommando ist sicherlich das s-Kommando. Damit kann man nach einem Muster suchen und dieses durch ein anderes Ersetzen.
Zum Beispiel ersetzt sed 's/rot/grün/' foobar die Zeichenfolge "rot" durch "grün" in der Datei foobar.
sed 's/rot/grün/' foobar
Der Fuchs ist grün und rot sind auch Äpfel.
Naja, aber eigentlich ist der Fuchs ja rot und Äpfel sind grün. Daher kann man dem s-Kommando noch zusätzliche Flags mitgeben. Das bekannteste Flag ist das 'g'. Damit werden alle gefundenen Muster ersetzt:
sed 's/rot/grün/g' foobar
Der Fuchs ist grün und grün sind auch Äpfel.
Man kann aber auch nur bestimmte Muster ersetzen, zum Beispiel nur das 2. Muster:
sed 's/rot/grün/2' foobar
Der Fuchs ist rot und grün sind auch Äpfel.
Wenn kein Flag angegeben wird, wird impliziert die 1 angenommen (also das erste gefundene Muster wird ersetzt).
Ein weiteres Flag ist das i. Hierbei wird das Muster case-insensitiv gesucht, d.h. Klein-/Großbuchstaben spielen keine Rolle. Es ist auch möglich mehrere Flags zu kombinieren:
sed 's/ROT/blau/ig' foobar
Der Fuchs ist blau und blau sind auch Äpfel.
Manchmal möchte man aber auch Pfadangaben ersetzen. Blöd nur, dass der Slash / normalerweise das Muster vom Ersetzungstext trennt. Möchte man nun z.B.
/usr/local/share/foobar durch /usr/share/foobar ersetzen, müßte man sowas nutzen:
sed 's/\/usr\/local\/share\/foobar/\/usr\/share\/foobar/g script
Daher erlaubt es sed, irgendeinen anderen Delimiter zu nutzen, z.B. die Pipe |:
sed 's|/usr/local/share/foobar|/usr/share/foobar|g script
oder den Unterstrich _
sed 's_/usr/local/share/foobar_/usr/share/foobar_g script
oder irgend ein anderes Zeichen, was nicht im Muster vorkommt.
Manchmal möchte man auch das gefundene Pattern im Ersetzungsteil wieder verwenden. Wenn man zum Beispiel eine Anweisung in einer Konfigurationsdatei auskommentieren möchte, die mit "foobar" anfängt kann man das so machen:
sed 's/^\(foobar\)/#\1/' config
#foobar wichtig!
Oder man benutzt einfach den Replacement Character &, der für das Muster steht, das gefunden wurde:
sed 's/^\(foobar\)/#&/' config
#foobar wichtig!
Vereinfachen kann man das noch so:
sed '/^foobar/s/^/#/' config
#foobar wichtig!
Hier benutzen wir den regulären Ausdruck /^foobar/ als Adresse und nur Zeilen, die auf dieses Muster passen, werden ersetzt. Vereinfacht ersetzen wir das Zeichen '^' (das für den Zeilenanfang steht) durch ein '#' und fügen somit eine Raute am Anfang der Zeile ein.
Man kann noch mehr mit regulären Ausdrücken machen. Zum Beispiel alle Großbuchstaben durch ihre Kleinbuchstaben ersetzen:
ls /home/ | sed 's/[[:lower:]]/\U&/g'
CB
CHRISBRA
FTP
Sed sucht nach der Posix-Klasse von Kleinbuchstaben und ersetzt diese durch die entsprechenden Großbuchstaben. Das \U steht für Uppercase und wandelt alle Zeichen in die passenden Großbuchstaben um. Man beachte die Nutzung von & um das gefundene Muster wiederzuverwenden. Ohne das Flag 'g' wäre übrigens nur der erste Buchstabe groß geschrieben worden. Das gleiche kann man übrigens mit dem Kommando y/ erreichen:
ls /home/ | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'
CB
CHRISBRA
FTP
Das ist etwas umständlich, denn das y-Kommando scheint keine Characterklassen anzunehmen, sondern man muß explizit alle Zeichen hinschreiben.
Das war das Wichtigste zum Substitute Kommando. Der nächste Teil kommt bestimmt ;)
