Sed: Texte automatisch verarbeiten

Linux Sed: Texte automatisch verarbeiten

Manchmal kommt es vor, dass man an einem Text-Dokument einige Zeichenketten löschen oder ändern möchte. Oft reicht es, die "Suchen und Ersetzen"-Funktion eines Editors zu benutzen. Aber es kommt auch vor, dass man mehrere Ersetzungen hintereinander vornehmen muss oder auch mehrere Dokumente zu verarbeiten hat, so dass man es einfach Leid ist, ständig die gleichen Ersetzungen durchzuführen. In einem solchen Falle ist es vielleicht ratsam, einfach die zu ersetzenden Zeichenketten in eine Datei zu schreiben, damit man mit einem einzigen Konsolen-Befehl die Ersetzungen durchführen kann. [LINK:START:195]Sed[LINK:END], der Stream-Editor ist hierfür die richtige Wahl. Hier soll es darum gehen, wie man sich ein kleines Sed-"Script" schreibt. bisher habe ich Sed nur als Werkzeug für meine Bash-Scripte eingesetzt. Die volle Mächtigkeit von Sed eröffnet sich einem aber erst, wenn man kleine (sehr einfache) Sed-"Scripte" abspeichert und staunt, wie einfach und schnell man damit alltägliche Aufgaben erschlagen kann. Eigentlich handelt es sich nicht um echte Scripte, weil sie nicht als ausführbar markiert werden, und man schreibt auch nicht Sed als Interpreter (also interpretierendes Programm) in den Kopf der Datei. Vielmehr sind es Konfigurations-Dateien, die man dann beim Aufruf von Sed verwendet, um sich Schreibarbeit zu sparen.

Funktion von Sed

Um Texte verarbeiten zu können, werden sie bei Sed zeilenweise in einen Puffer geladen, der dann bearbeitet wird. Die angegebenen Kommandos gelten also, von wenigen Ausnahmen abgesehen, jeweils für eine Zeile. Die Texte werden beispielsweise von der Standard-Eingabe gelesen, anhand vorgegebener Regeln verarbeitet und das Ergebnis auf die Ausgabe geschrieben. Diese vorgegebenen Regeln können recht vielfältig sein.

Ersetzungen

Als Beispiel sollen einfach mal einige Umlaute eines Textes in andere, bei HTML übliche Zeichenketten umgewandelt werden. Dies geschieht jeweils mit einem so genannten Ersetzung-Kommando:

s/DIES/DAS/gp;

Dabei wird hier "DIES" durch "DAS" ersetzt. Das "s" am Anfang leitet das Kommando ein. "S" steht hier für Substitution, als Ersetzung. Das g am Ende steht für eine globale Ersetzung. Es werden also alle Vorkommen in der verarbeiteten Zeile ersetzt. Die Slashes "/" sind nur Trennzeichen und können ggf. auch durch andere Symbole wie "!" ersetzt werden. Zwischen dem linken und mittleren Trennzeichen steht der Text, der weg soll und rechts neben dem mittleren Trennzeichen der Text, der statt dessen hingeschrieben werden soll. Ein "p" am Ende meint, dass die Zeile nach der Verarbeitung ausgegeben werden soll.

Allein mit diesem Kommando kann man also schon alle Umlaute in einem Text ersetzen:

# sed-Script zum Konvertieren von ASCII-Texte in HTML-Texte.
# Die Formatierung mit Tags muss man natürlich noch vornehmen.
s/&/\&/g;
s/ä/\ä/g;
s/Ä/\Ä/g;
s/ü/\ü/g;
s/Ü/\Ü/g;
s/ö/\ö/g;
s/Ö/\Ö/g;
s/ß/\ß/g;
s/"/\"/g;
# Dieses p steht dafür, dass jetzt die Zeile
# ausgegeben wird.
p; 

Hier werden auch noch Sonderzeichen wie das kaufmännische und "&" sowie die Häkchen durch speziellen HTML-Code ersetzt. Dabei ist auch darauf zu achten, dass die Reihenfolge der Ersetzungen eine Rolle spielen kann. Denn würde man das "&" erst nach den Umlauten ersetzten, so wären davon auch die vorangestellten "&" im HTML-Code betroffen. Speichert man obigen Abschnitt in einer Datei namens txt2html.sed, dann kann man eine Datei (hier: Text.txt) folgendermaßen konvertieren:

sed -n -f txt2html.sed Text.txt

Man stellt fest, dass dabei das Ergebnis auf der Standard-Ausgabe erscheint. Hier wird die Option "-n" verwendet, die Sed mitteilt, dass es nur dann die aktuelle Zeile ausgeben soll, wenn dies ausdrücklich in der Konfigurations-Datei angegeben wird. Die Ausgabe kann man dann ganz einfach in eine Datei umleiten:

sed -n -f txt2html.sed Text.txt > Text.pre.html

Hier soll das "pre" andeuten, dass es sich natürlich nicht um eine fertige HTML-Datei handelt. Dies wird man auch nur "von Hand" erledigen können, da ja noch Informationen zur Formatierung des Textes hinzugefügt werden müssen.

Umgekehrt kann es auch mal sein, dass man aus einer HTML-Datei eine reine ASCII Text-Datei machen will. Vor allem wird man dabei die HTML-Tags (das sind diese komischen Dinger mit den spitzen Klammern) entfernen wollen. Natürlich ist es auch denkbar, dass man die Tags in andere Kommandos verwandelt, die dann beispielsweise für Latex die passenden Informationen enthalten. Wenn man aber einen echten Übersetzer mit Sed bauen will, wird man dabei sicherlich schnell komplizierte Ausdrücke erhalten und an die Grenzen von Sed stoßen. Hier ist ein Vorschlag zum Löschen von HTML-Tags und zur Umwandlung der Umlaute:

# sed-Script zum Konvertieren von HTML-Texte in ASCII-Texte.
s/<[^>]*>//g 
s/&auml;/ä/g;
s/&Auml;/Ä/g;
s/&uuml;/ü/g;
s/&Uuml;/Ü/g;
s/&ouml;/ö/g;
s/&Ouml;/Ö/g;
s/&szlig;/ß/g;
s/&quot;/"/g;
s/&amp;/&/g;
s/&lt;/</g;
s/&gt;/>/g;
s/&nbsp;/ /g;
p;

Das Kommando zum Aufruf von Sed bleibt abgesehen von den Dateinamen gleich. Die eckigen Klammern definieren eine Zeichen-Klasse, also Zeichen, die vorkommen dürfen. Dabei dient "^" der Negierung: Das nachfolgende Zeichen ist nicht enthalten. "[^>]" steht also für alle Zeichen außer ">". Das Sternchen danach bedeutet, dass diese Zeichen in beliebiger Reihenfolge beliebig oft vorkommen dürfen.

Gruppierungen

Ein weiteres Beispiel für den Sinnvollen Einsatz von Sed ist eine Internet-Seite, die eine große Tabelle mit Links zu PDF-Dokumenten enthält. Dabei werden etwa 260 alte Klausuren und deren Musterlösungen verlinkt. Man könnte sich natürlich die Mühe machen, hier einfach 260 Mal die Maus zu drücken und den Datei-Dialog zu bestätigen, um alles per Browser runter zu laden. Einfacher ist es da, die besagte Tabelle als HTML-Datei zu speichern und mit einer lächerlich einfachen Sed-Konfiguration die Links-Ziele zu extrahieren, von denen hier in jeder Zeile der HTML-Datei nur eines steht:

sed -n -e 's/\(^.*<a href="\)\([^"]*pdf\)\(".*$\)/\2/gp'  KlausurenTabelle.html > dateien.txt

Wie man sieht, braucht man dafür nicht einmal eine Konfigurations-Datei. Man kann das Ersetzungs-Kommando mit einem "-e" als Option direkt in der Kommando-Zeile angeben. Man erhält auf diese Weise eine Datei, bei der in jeder Zeile der Name einer PDF-Datei steht. Mit Hilfe von wget, einem Download-Programm für die Konsole, einer for-Schleife kann man nun die Dateien runter laden:

for datei in $(cat dateien.txt) ; do wget http://www.meineuni.de/FACHSCHINF/$datei ; done

Auf diese Weise kann man in der Zwischenzeit schon mal ein Käffchen trinken ... Dir wird aufgefallen sein, dass hier das Sed-Kommando etwas anders ausfällt. Mit Hilfe von "\(" und "\)" werden hier insgesamt drei Ausdrücke in ein Ersetzungs-Kommando geschrieben. Der erste trifft auf alle Zeichen vom Zeilenanfang bis zum Beginn des in dem HTML-Tag befindlichen Dateinamen zu. Der in der Mitte trifft auf den Datei-Namen einer PDF-Datei zu und der letzte auf den Rest der Zeile. Deshalb funktioniert dieses Kommando hier auch nur, wenn in jeder Zeile höchstens eine PDF-Datei verlinkt wird. Statt eines festen Textes, der statt des gefunden hingeschrieben werden soll, steht hier "\2". Dies steht für den mittleren Ausdruck, also für den Datei-Namen. Hätte man "\1" verwendet, so würde man jeweils den Zeilenanfang und bei "\3" den Rest der Zeile erhalten. Die Sonderzeichen "^" und "$" stehen hier für den Zeilenanfang bzw. das Zeilenende. Damit hat "^" zwei Bedeutungen: Steht es in eckigen Klammern, so wird das nachfolgende Zeichen ausgeschlossen, darf also nicht vorkommen. Steht es nicht in eckigen Klammern, ist damit der Zeilenanfang gemeint. Ein einfacher Punkt "." steht für jedes beliebige Zeichen.

Wenn man jetzt beispielsweise den gesamten Text ausgeben möchte, der anhand der linken Seite des Ersetzungs-Ausdrucks gefunden wurde, dann kann man dies mit "&" tun:

sed -e 's/<a[^>]*href[^>]*>[^<]*<\/a>/<!-- & -->/g' SeiteMitLinks.html > SeiteOhneLinks.html 

Obiges Kommando kommentiert alle Links einer HTML-Seite aus, so dass diese im Browser unsichtbar werden, aber nicht komplett gelöscht sind.

Muster-Erkennung

Ähnlich einfach funktioniert ein Sed-Script, welches nur den Header (also den Kopf) einer Mail ausgeben soll. Es gibt einfach alle Zeilen aus, bis es eine komplett leere Zeile findet. Dann bricht es schlicht mit dem Kommando "q" ab:

/^$/{;
q;
};
p;

Das einzig Besondere hieran ist, dass mit den speziellen Symbolen "^" und "$", nach einer leeren Zeile gesucht wird. Dazu wird das Kommando zur Muster-Erkennung verwendet:

/GEFUNDEN/{;
...
};

Kommt der Text "GEFUNDEN" in der aktuellen Zeile vor, werden die Befehle in den Klammern ausgeführt, die hier durch Pünktchen angedeutet sind.

Sprung-Befehle machen Spaß

Manchmal kommt man nicht ohne den Einsatz von Sprung-Befehlen aus. Mit ihnen kann man Teile in seiner Sed-Konfiguration überspringen, so dass man je nach Zeilen-Typ (erkennbar anhand von Mustern/Ersetzungen wie oben beschrieben) eine unterschiedliche Verarbeitung festlegen kann. Dazu kann man in seinem Sed-Script Marken definieren, die dann mit bestimmten Befehlen angesprungen werden können. Bei der Definition einer Marke stellt man einfach einen Doppelpunkt voran

: DiesIstMeineMarke;

Die einfachste Form des Sprungbefehls unterscheidet sich nur darin von der Definition, dass statt des Doppelpunktes ein "b" Verwendung findet. Mit

b DiesIstMeineMarke;

veranlasst man sed also zu der Marke "DiesIstMeineMarke" zu springen und dort die Verarbeitung fortzusetzen. Dieser Sprung geschieht auf jeden Fall. Will man eine Bedingte Verzweigung haben, so hat man die Möglichkeit, nur dann zur Marke zu springen, wenn eine Ersetzung durchgeführt wurde. Man nimmt dann einfach ein "t".

s/DIES/DAS/g;
t DiesIstMeineMarke;

Konnte hier also kein Wort "DIES" in der aktuellen Zeile gefunden werden, wird die Verarbeitung ganz normal (also von oben nach unten im Script) fortgesetzt. In nachstehendem Beispiel werden die Sprung-Befehle verwendet, um zu verhindern, dass nach einer erfolgten Ersetzung von To: nach Reply-To: erneut eine Ersetzung durchgeführt wird und wieder To: entsteht:

# vertauscht Absender und Empfänger einer eMail.
# Aufruf ohne "-n" Option.
s/^To:/Reply-To:/;
t;
s/^Reply-To:/To:/;

Finito

So, das soll's erstmal als Einstieg gewesen sein. Reguläre Ausdrücke wie die von Sed verwendeten sind ein weites Feld und allein deshalb kann dieses Dokument nicht alles enthalten. Aber wenn du mal ein kleines Problem wie eines der hier beschriebenen hast, kannst du hier zumindest schon mal eine kleine Hilfe finden. Ansonsten findet man auch mit Hilfe einschlägiger Suchmaschinen die ein oder andere Sed-Konfiguration. Nicht zu vergessen ist natürlich auch die man-Page (man sed) von Sed, die GNU [LINK:START:195]WebSite[LINK:END] zu Sed und der [LINK:START:196]Anhang[LINK:END] des Advanced Bash-Scripting Guide. Deutsche Dokumentation findet man beispielsweise bei der [LINK:START:197]Linux-AG[LINK:END] und in Form eines [LINK:START:198]Artikels[LINK:END] auf der WebSite des Linux-User Magazins.

Artikelbewertung: 
No votes yet
War dieser Artikel hilfreich?