# 1.103.2

# Texte mittels Filterprogrammen bearbeiten

Beschreibung: Prüfungskandidaten sollten in der Lage sein, Filter auf Textströme anzuwenden. Dieses Lernziel beinhaltet das Senden von Textdateien und -ausgaben durch Textfilterprogramme, um die Ausgabe zu modifizieren, und die Verwendung von Standard-Unix-Kommandos, die im GNU textutils Paket enthalten sind.

Die wichtigsten Dateien, Bezeichnungen und Anwendungen:

- cat
- cut
- expand
- fmt
- head
- join
- nl
- od
- paste
- pr
- sed
- sort
- split
- tac
- tail
- tr
- unexpand
- uniq
- wc

### Textfilter

Unter Unix werden sehr viele kleine Programme angeboten, die als Filter für Textdateien zur Anwendung kommen und die in der Regel nur eine kleine Aufgabe beherrschen. Durch die Kombination mehrerer solcher Programme mit Pipes und Umleitungen (siehe Abschnitt 1.103.4 - Benutzen von Unix Streams, Pipes und Umleitungen) können dadurch vielfältige Aufgaben gelöst werden. Die in dieser Zielsetzung genannten Textfilter sollten Sie beherrschen. Hier nochmal zu diesen Filtern ein paar Kurzbeschreibungen. Grundsätzlich ist das Studium der jeweiligen Handbuchseiten hier sehr empfehlenswert...

### sed

sed ist ein Stream-Editor, ein Editor, der Datenströme oder Dateien nach bestimmten Regeln bearbeitet. sed ist ein sehr komplexes Programm, von dem in der LPI-101-Prüfung nur sehr geringe Kenntnisse verlangt werden. Wichtig ist zu wissen, daß dieses Werkzeug in der Lage ist, immer wiederkehrende Aufgaben mit Hilfe einer Scriptdatei zu lösen.

Ein Beispiel mag das verdeutlichen:

Sie haben 500 Textdateien, in denen immer wieder der Begriff "Synopsis" vorkommt. Sie wollen diesen Begriff nach "Syntax" ändern. Außerdem soll jeweils die 3. Zeile jeder dieser Dateien gelöscht werden. Das wäre unglaublich viel Arbeit, hätten wir nicht das Programm sed. Der Streameditor erlaubt uns verschiedene Aktionen auf einen Datenstrom auszuführen. Dazu erstellen wir eine Datei, die die notwendigen Befehle enthält. Nennen wir sie "befehle":

      1,$s/Synopsis/Syntax/g
      3d

Jede Zeile enthält einen Befehl. Jeder Befehl besteht aus einer Adress-Angabe und dem Befehl selbst. Die erste Zeile hat die Adressangabe `1,$`, was soviel heißt wie von der ersten Zeile bis zum Dateiende. Der Befehl ist `s` - das steht für substitute - also ersetzen. Der Befehl s hat die Form:

`s/`Suchbegriff`/`Ersetzungsbegriff`/`Optionen

Als Option haben wir `g` benutzt, was heißt, daß eine Zeile global durchsucht werden soll, nicht nur das erste Auftreten, sondern jedes soll ersetzt werden.

Die zweite Zeile unserer Datei ist noch einfacher, ihr Adress-Teil ist einfach die 3 und meint also Zeile 3. Der Befehl ist `d` und steht für delete also löschen. Es ist also die Anweisung, die Zeile 3 zu löschen.

Um diese Befehlsdatei jetzt anzuwenden, schreiben wir nur noch:

      sed -f befehle Datei > Datei_neu

und schon wurde die Datei `Datei` bearbeitet. Das Ergebnis steht jetzt in `Datei_neu`. Eine kleine Schleife konstruiert, und schon könnten wir alle 500 Dateien mit einem Aufwasch bearbeiten...

Wie gesagt, die LPI 101-Prüfung besteht nicht darauf, daß Sie den sed vollständig beherrschen, aber ein paar Übungen dazu sind nicht verkehrt. Genauere Beschreibungen, was dieses Programm für Befehle versteht finden Sie auf der sed-Handbuchseite.

### sort

Dieses kleine Programm sortiert einen Eingabedatenstrom oder eine Datei zeilenweise und gibt das Ergebnis wieder auf die Standard-Ausgabe aus.

Normalerweise werden die ganzen Zeilen verglichen, um sie zu sortieren. Es ist aber auch möglich, Zeilen nach einem bestimmten Positionsfeld zu sortieren. Ein Beispiel:

Der Befehl `ls -l` gibt uns eine Liste aller Dateien im aktuellen Verzeichnis aus. Ab Spalte 31 steht die Dateigröße. Würden wir den Befehl

      ls -l | sort

eingeben, dann würden die gesamten Zeilen sortiert. Das macht wenig Sinn, denn die Zeilen beginnen ja immer mit der Angabe der Zugriffsrechte und die zu sortieren ist sicher nicht das, was wir wollen. Um die Ausgabe nach Dateigröße zu sortieren können wir eingeben:

      ls -l | sort +0.32

Dabei bedeutet `0.32` das erste Feld (0) und davon alles, ab dem 31. Buchstaben. (Die Angabe von Feldern bezieht sich auf Dateien, die tatsächlich feldorientiert aufgebaut sind, wie etwa `/etc/passwd`)

Alle Optionen und Möglichkeiten entnehmen Sie wieder der sort-Handbuchseite.

### uniq

uniq ist ein Programm, das aus einer Datei (oder einem Eingabedatenstrom) hintereinanderliegende gleiche Zeilen löscht, so daß nur noch eine Zeile übrig bleibt. Die Betonung liegt auf hintereinanderliegend. Am häufigsten wird uniq in Kombination mit sort eingesetzt, weil gerade beim sortieren häufig vorhandene Zeilen hintereinander geschrieben werden. Hat eine Datei (oder ein Datenstrom) z.B. viele Leerzeilen, so werden all diese Leerzeilen hintereinander in die sortierte Ausgabe geschrieben. Mit uniq können diese Leerzeilen auf eine Zeile reduziert werden.

Über Kommandozeilenparameter kann zudem eingestellt werden, daß nur bestimmte Teile einer Zeile verglichen werden, um die Gleichheit festzustellen. Die genauen Parameter sind der uniq-Handbuchseite zu entnehmen.

### cut

Mit cut werden bestimmte Spalten einer Datei ausgeschnitten und auf die Standard-Ausgabe geschrieben. Dabei können Spalten entweder durch absolute Positionen angegeben werden, oder als Felder, die durch bestimmte Trennzeichen abgegrenzt sind.

Um z.B. alle Usernamen aus der Datei /etc/passwd zu schneiden benutzen wir die Feldbegrenzer `:` und wollen nur das erste Feld sehen:

      cut -d: -f1 /etc/passwd

Wenn wir aus der Liste aller laufender Prozesse die PIDs ausschneiden wollen, so müssen wir das mit absoluten Positionsangaben machen. Die Ausgabe von ps huax sieht ja etwa so aus:

    at         172  0.0  0.4   892   448  ?  S    10:09   0:00 /usr/sbin/atd
    bin        128  0.0  0.3   816   360  ?  S    10:09   0:00 /sbin/portmap
    ...

Die ProzeßIDs stehen also von Zeichen 9 bis 14. Um genau diese Spalte auszuschneiden schreiben wir also:

      ps huax | cut -c9-14

So bietet uns cut also die Möglichkeit, jeden beliebigen Teil einer Zeile auszuschneiden und auf die Standard-Ausgabe zu schreiben. Das ist natürlich hervorragend geeignet, um in Pipes weiterverarbeitet zu werden. Die genaue Beschreibung entnehmen Sie wieder der cut-Handbuchseite

### expand

Das Programm expand wird benutzt, um Tabulatorzeichen aus einer Textdatei oder einem Eingabedatenstrom durch eine beliebige Menge Leerzeichen im Ausgabedatenstrom zu ersetzen. Das klingt trivial, ist aber sehr häufig gebraucht, um Listen, die durch Tabs getrennt sind in ein druckbares Format zu verwandeln.

Die genaue Beschreibung ist der expand-Handbuchseite zu entnehmen.

### unexpand

unexpand arbeitet genau gegenläufig zu expand. Es verwandelt eine bestimmte Menge Leerzeichen aus einer Datei (oder einem Eingabedatenstrom) in Tabulatorschritte. Dabei kann mit Parametern angegeben werden, wieviele Leerzeichen jeweils in einen Tab verwandelt werden sollen.

Die genaue Beschreibung ist der unexpand-Handbuchseite zu entnehmen.

### fmt

fmt (steht für format) ist ein Programm, das Textdateien oder Eingabedatenströme absatzweise formatiert. Dabei sind Einstellungen möglich, wie etwa die Breite jedes Absatzes, die Anfangseinrückung oder das Zusammenziehen von mehreren Leerzeichen. So können etwa Textdateien mit Zeilenlängen über 80 für eine Druckausgabe auf eine Zeilenbreite von 75 umgewandelt werden.

Die genaue Beschreibung ist der fmt-Handbuchseite zu entnehmen.

### head

Head schreibt die ersten Zeilen einer Datei oder eines Eingabedatenstroms auf die Standard-Ausgabe. Ohne Parameter werden die ersten 10 Zeilen ausgegeben.

Durch Kommandozeilenparameter kann bestimmt werden, wieviele Zeilen oder Bytes vom Anfang der Datei ausgegeben werden sollen. Dazu wird die gewünschte Anzahl Zeilen einfach mit einem Bindestrich versehen an den Befehl angehängt.

Die genaue Beschreibung ist der head-Handbuchseite zu entnehmen.

### tail

Tail arbeitet wie head, nur daß nicht die ersten, sondern die letzten Zeilen einer Datei oder eines Eingabedatenstroms ausgegeben werden. Auch hier kann die gewünschte Anzahl der zu zeigenden Zeilen angegeben werden.

In Kombination mit head können damit beliebige Teile einer Datei ausgeschnitten werden. Wollen wir z.B. die Zeilen 7-12 der Datei `versuch.txt`, so können wir zuerst mit head die ersten 12 Zeilen der Datei auschneiden und uns dann daraus die letzten 6 Zeilen mit tail entnehmen. Elegant wird das in einer Pipe:

      cat versuch.txt | head -12l | tail -6l

tail hat noch den speziellen Parameter -f, der es anweist, nicht nach der Ausgabe der letzten 10 Zeilen abzubrechen, sondern zu warten, ob die Datei noch weiter wächst und die dann angehängten Zeilen ebenfalls auszugeben. Wollen wir so etwa die Meldungen des Syslog-Daemon ständig überwachen, so schreiben wir:

      tail -f /var/log/messages

Solange wir tail nicht mit Strg-C abbrechen, wird es nun die Datei /var/log/messages dauernd überwachen und jede neue Zeile auf dem Bildschirm anzeigen. Wir können also der Datei "beim Wachsen zusehen".

Die genaue Beschreibung ist der tail-Handbuchseite zu entnehmen.

### join

join verknüpft zwei (alphabetisch) sortierte Dateien, indem je zwei Zeilen mit identischen Schlüsselfeldern zu einer Ausgabezeile verbunden werden.

Die Schlüsselfelder sind - wenn nicht anders angegeben - durch Leerzeichen voneinander getrennt. Führende Leerzeichen werden ignoriert. Wenn nicht anders angegeben, ist das erste Feld einer jeden Zeile Schlüsselfeld. Die Ausgabefelder sind ebenfalls durch Leerzeichen voneinander getrennt. Die Ausgabe besteht aus dem Schlüsselfeld, gefolgt von den übrigen Feldern der Datei1 und schließlich aller Felder der passenden Zeilen von Datei2 ohne das Schlüsselfeld.

Damit können verschiedene Dateien nach inhaltlichen Kriterien zusammengefügt werden. Nehmen wir ein Beispiel:

Die Datei `Adressen.txt` beinhaltet Zeilen wie die folgenden:

      ...
      Huber Peter Schillerstr. 23 54321 Musterhausen
      Maier Hans Goethestr. 3 12345 Musterstadt
      Schmidt Stefan Kleistweg 34 55555 Musterhofen
      ...

Die Datei `Jobs.txt` enthält jetzt etwa

      Huber Geschäftsführer
      Kohler Fensterputzer
      Schmidt Aufzugführer

Wenn wir jetzt beide Dateien mit dem Befehl

      join Adressen.txt Jobs.txt

miteinander verknüpfen, so werden nur die Zeilen miteinander verbunden, die gemeinsame Schlüsselfelder haben. Voreingestellt ist das erste Feld, also der Name. Das Ergebnis wäre:

      Huber Peter Schillerstr. 23 54321 Musterhausen Geschäftsführer
      Schmidt Stefan Kleistweg 34 55555 Musterhofen Aufzugführer

Die genaue Beschreibung ist der join-Handbuchseite zu entnehmen.

### nl

nl gibt die Zeilen einer oder mehrerer Dateien (oder der Standardeingabe) mit Zeilennummern auf die Standardausgabe. Es können dabei die Zeilen einer (logischen) Seite in einen Kopf, einen Körper und einen Fuß unterteilt werden, die jeweils einzeln und in unterschiedlichen Stilen nummeriert werden. Jeder Teil kann auch leer sein. Wenn vor dem ersten Kopfteil bereits Zeilen vorhanden sind, werden diese Zeilen wie ein Seitenkörper nummeriert.

Die Nummerierung beginnt auf jeder Seite neu. Mehrere Dateien werden als ein einziges Dokument betrachtet, und die Zeilennummer nicht zurückgesetzt.

Der Kopfteil wird durch eine Zeile eingeleitet, die nur die Zeichenkette \:\:\: enthält. Der Körper wird entsprechend durch \:\: und der Fuß durch \: eingeleitet. In der Ausgabe werden diese Zeilen als Leerzeilen ausgegeben.

Die genaue Beschreibung ist der nl-Handbuchseite zu entnehmen.

### od

od (Octal Dump) gibt Dateien oder einen Eingabedatenstrom als dezimalen, oktalen oder hexadezimalen Dump aus. So können Binärdateien in eine lesbare Form gebracht werden. Jede Ausgabezeile enthält neben der eigentlichen Bytedarstellung am Zeilenanfang noch ein siebenstelliges Adressfeld.

Die genaue Beschreibung ist der od-Handbuchseite zu entnehmen.

### paste

paste verknüpft zwei oder mehrere Dateien zeilenweise, so daß die ersten Zeilen der Dateien zur ersten Zeile der Ausgabedatei werden.

Damit können also einzelne Dateien, die Spalten enthalten, die etwa von cut erzeugt wurden, wieder zu einer Datei zusammengesetzt werden. Nehmen wir ein simples Beispiel:

Wir wollen eine Datei erstellen, in der in der ersten Spalte die UserID, in der zweiten der Username steht. Die Datei soll alle User des Systems enthalten. Wir können das nicht allein mit cut erledigen, weil ja in /etc/passwd zuerst der Username und erst im 3. Feld die UserID steht. Zunächst erzeugen wir also zwei Dateien, die jeweils nur eine Spalte der /etc/passwd beinhalten, dann fügen wir diese beiden Dateien umgekehrt wieder zusammen:

      cut -d: -f1 /etc/passwd > Datei1
      cut -d: -f3 /etc/passwd > Datei2
      paste -d: Datei2 Datei1 > Datei3

Im Gegensatz zu join (siehe oben) werden die Zeilen der angegebenen Dateien ohne inhaltliches Kriterium miteinander verknüpft. Die genaue Beschreibung ist der paste-Handbuchseite zu entnehmen.

### pr

pr dient zur Formatierung von Textdateien für die Druckerausgabe. pr wird heute nur noch selten verwendet, es gibt einfach andere, bessere Druckerfilter. Trotzdem kann es manchmal noch gute Dienste leisten.

Immerhin bietet pr auch Features wie Spaltensatz, Seitennummerierung und Kopfzeilen, genaueres siehe bei der pr-Handbuchseite

### split

Die Anwendung von split ist einfach und sie wird häufig gebraucht, um z.B. große Dateien in viele kleinere aufzuteilen, damit sie mit Disketten transportiert werden können. Die Größe der anzulegenden Zieldateien kann sowohl in Zeilen, als auch in Bytes angegeben werden.

Die kleineren Zieldateien heißen standardmäßig xaa, xab, xac, ..., wobei - falls die Buchstaben nicht ausreichen - auch mehrere Buchstaben verwendet werden (xaaaa, xaaab,...). Weil das ein wenig unhandlich ist, kann man dem split-Programm ein Präfix mitgeben, das dann das x ersetzt.

Um also z.B. eine Datei netscape.tgz mit ca 13 MByte in Dateien der Größe 1 MByte aufzuteilen, um sie auf Disketten verteilen zu können, schreiben wir:

      split -b 1m netscape.tgz

Als Ergebnis bekommen wir jetzt die Dateien xaa, xab, xac, ... Das ist verwirrend - daher benutzen wir eben das Präfix:

      split -b 1m netscape.tgz netscape_

Jetzt bekommen wir die Dateien netscape_aa, netscape_ab, ...

Um diese Dateien wieder zusammenzubauen benutzen wir einfach das cat-Programm. wir könnten so also schreiben:

      cat netscape_aa netscape_ab netscape_ac netscape_ad netscape_ae > netscape.tgz

Das ist zugegebenerweise etwas unhandlich, aber jetzt kommt der Vorteil ins Spiel, daß die Shell alphabetisch ihre Ausgaben sortiert. Weil die aa, ab, ac ... Aufteilung streng alphabetisch ist, können wir auch viel einfacher schreiben:

      cat netscape_* > netscape.tgz

Die genaue Beschreibung ist der split-Handbuchseite zu entnehmen.

### cat

Das Programm cat arbeitet ähnlich wie das DOS-Programm TYPE. Es gibt die Dateien, die als Parameter angegeben wurden auf der Standard-Ausgabe aus. Falls keine Parameter angegeben wurden, so nimmt cat die Standard-Eingabe als Datenstrom an, der auf der Standar-Ausgabe ausgegeben werden soll.

Das klingt auf den ersten Blick trivial, wird aber sehr häufig gebraucht, um Dateien in eine Pipe oder auf ein Gerät zu schicken:

      cat datei.txt > /dev/tty10

Schickt die Datei datei.txt statt auf die Standard-Ausgabe in die Gerätedatei /dev/tty10 und damit auf das Terminal 10.

Außerdem können mit cat mehrere Dateien zu einer zusammengefügt werden, indem die Ausgabe wieder in eine Datei umgeleitet wird:

      cat teil1.txt teil2.txt teil3.txt > gesamt.txt

Mit Optionsschaltern kann cat auch dazu gebracht werden, mehrere aufeinanderfolgende Leerzeilen zu einer Leerzeile zu reduzieren oder Zeilen in der Ausgabe zu nummerieren.

Die genaue Beschreibung ist der cat-Handbuchseite zu entnehmen.

### tac

Das Programm tac (cat umgekehrt geschrieben) gibt Dateien in umgekehrter Reihenfolge aus, also die letzte Zeile zuerst, dann die vorletzte usw. Dabei müssen es nicht zwangsläufig Zeilen sein, tac kann auch andere Zeichen als den Zeilentrenner als Trennzeichen benutzen.

Die genaue Beschreibung ist der tac-Handbuchseite zu entnehmen.

### tr

tr (translate) löscht oder ändert einzelne Zeichen eines Eingabedatenstroms und schreibt das Ergebnis wieder auf die Standard-Ausgabe.

Die Angabe der zu übersetzenden Zeichen erfolgt dabei in sogenannten Mengen. Eine Menge ist einfach eine Zeichenkette aus Buchstaben. Um z.B. in einer Datei alle vorkommenden a,b und x in A,B und X zu verwandeln, benutzen wir die Mengen "abx" und "ABX".

tr ist ein reiner Filter, d.h., das Programm kann ausschließlich in Pipes verwendet werden. Wenn wir also die Datei `Test.txt` übersetzen wollen, so müssen wir schreiben:

      cat Test.txt | tr abx ABX > Ergebnis

Zugegebenermaßen ein unsinniges Beispiel. tr erlaubt es aber auch, Steuerzeichen und andere undruckbare Zeichen in den Mengen zu verwenden, indem ihr Oktalwert angegeben wird. Dazu muß dem Wert ein Backslash vorangestellt werden. Hier sind dann aber auch Anführungszeichen nötig, sonst würde die Shell uns den Backslash weginterpretieren.

Außerdem bietet tr mehrere bereits vordefinierte Mengen, wie etwa alle Großbuchstaben und alle Kleinbuchstaben. So können wir eine Datei z.B. in Großbuchstaben verwandeln indem wir schreiben:

      cat Test.txt | tr [:lower:] [:upper:] > Ergebnis

Die genaue Beschreibung ist der tr-Handbuchseite zu entnehmen.

### wc

Dieses Programm (word count) zählt Zeichen, Wörter und Zeilen einer Datei oder eines Eingabedatenstroms. Es ist in der LPI 101 Prüfung relativ häufig gefragt, aber auch wirklich sehr einfach anzuwenden.

Ohne Parameter aufgerufen gibt wc alle drei Angaben aus, in der Reihenfolge Zeilen, Wörter, Zeichen. wc kennt aber auch Parameter, die es veranlassen nur die Zeilen, Wörter oder Zeichen zu zählen. Es gibt diese Parameter jeweils in einer langen und kurzen Form, aber auch jeweils leicht zu merken und zu verstehen:

- `-l` oder `--lines` zählt nur die Zeilen
- `-w` oder `--words` zählt nur die Wörter
- `-c` oder `--chars` zählt nur die Zeichen

Die genaue Beschreibung ist der wc-Handbuchseite zu entnehmen.