Das Kommando »cat« betört Kommandozeilen-Liebhaber mit seiner schlichten Eleganz. Für manche ist es Dateibetrachter, Textmanipulator oder das einfachste denkbare Eingabeprogramm. Gerüchteweise haben einige sogar schon Binarys damit eingetippt. Jetzt zeigt sich: Shell-Programmierer nutzen das Tool zu oft .
Linuxer gelten als tippfaul und effizienzliebend. Dafür gibt es viele Anwendungsgebiete. Da die Tastatur das wichtigste Eingabegerät neben der Maus ist, bietet sie sich herrvorragend als Betätigungsfeld an. Aus der Überlegunug heraus entstand das Neo-Keyboardlayout [1], das die Tasten ausgehend von den Erfahrungen mit dem Dvorak-Layout und einigen statistischen Überlegungen neu anordnet (siehe Abbildung 1).

Abbildung 1: Das Neo-Layout ordnet die Tasten komplett neu an. Es kennt insgesamt sechs Ebenen, um Zeichen zu erzeugen.
Neu im Vergleich zur klassischen Qwertz-Form ist, dass Neo insgesamt sechs Tastenebenen kennt statt nur zwei. Fans der Anordnung nehmen für sich in Anspruch, erheblich schneller tippen zu können. Vor neuen Schreibrekorden steht jedoch die Lern- und Umgewöhnungsphase. Hier hilft Das von Martin Engel verfasste Skript »xmodmap2tastenaufkleber.sh« [2] hilft, die einzelnen Tastenkombinationen von einer per »xmodmap -pke« erzeugten Belegung in Schaubilder umzusetzen (siehe Abbildung 2).

Abbildung 2: Wer beim Neo-Layout nicht den Überblick verlieren will, dem hilft die Tafel von »xmodmap2tastenaufkleber.sh«, die eine Tastaturbelegung in eine SVG-Datei umwandelt.
Wer jedoch das Skript unter die Lupe nimmt, reibt sich verwundert die Augen. Da verwendet der Autor gleich mehrfach den »cat«-Befehl, um eine Pipe mit notwendigem Input zu versorgen (siehe Listing 1).
| Listing 1: Unnecessary use of »cat« |
|---|
01 [...] 02 #================================================ 03 # Herausfiltern von Modifikatortasten, etc. 04 # Die zu filternden Tastencodes stehen in den runden 05 # Klammern jeweils durch ein Oder-Zeichen getrennt 06 07 cat tmp_$1 | grep -v -E "^(9|51|65|66|94|113|115|116)[^0-9]" > tmp_$1 |
Missverstanden
Das Beispiel demonstriert anschaulich, wieso »cat« vermutlich der am häufigsten von Shellprogrammierer missverstandene Befehl ist: Sein Name ist eine Unix-typische Verkürzung von “Concatenation”, zu deutsch Zusammenfügen. Dazu gehören aber mindestens zwei, so sucht
cat *.report | sort | head -3
die drei lexikalisch ersten Einträge aus einer ganzen Sammlung von Berichten heraus. Aber selbst dieses Beispiel ist unnötig, denn viele der Basisbefehle verstehen mehr als ein einzelnes Argument, so auch »sort«: das kürzere
sort *.report | head -3
täte es auch. Das in Zeile 7 von Listing 1 verwendete »grep« kann selbst Eingabedateien öffnen, mit »grep -E […] “tmp_$1″« erreichte der Autor prinzipiell das Gleiche. Weil er aber seine temporäre Datei nicht in Doublequotes kleidet, scheitert das Programm an Argumenten, die etwa Leerzeichen im Namen tragen [3]. Schwerwiegender ist allerdings, dass das Ergebnis der Pipe in derselben Zeile wieder in seine Eingabedatei schreibt. Das kann nicht verlässlich funktionieren. Sobald die Bash eine solche Kette interpretiert, öffnet sie die letzte angegebene Datei mit der Open-Optionen »O_TRUNC« und sägt sich damit praktisch selbst den Ast ab, auf dem sie sitzt.
Teure Prozesse
Nicht alle Kommandozeilentools beherrschen jedoch beliebig viele Dateiargumente – einige noch nicht einmal eine einzige Eingabedatei. Das Programm zum zeichenweisen Ersetzen, »tr«, ist so ein Beispiel, es akzeptiert seine Eingabe ausschließlich aus der Standardeingabe. Ist hier der Bash-Programmierer also auf »cat« angewiesen und muss einen wertvollen Prozess starten? Mitnichten, denn die Shell kennt dafür die Form
tr 'A-Z' 'N-ZA-M' < input.txt > output.txt
um beispielsweise ein Rot13 für Großbuchstaben zu implementieren. Wer den Inhalt einer Datei in einer Shellvariable braucht, darf das übrigens mit
indatei=input.txt daten=$(<$indatei)
formulieren. Gerade in Schleifen sind immer neu gestartete Unterprozesse für jede Menge Ressourcenverbrauch verantwortlich. Ein kurzer Benchmark durchsuchte knapp 3 300 Dateien nach einem Suchstring. Die Variante in Listing 2 braucht daher 3 300 Prozesse mehr als die kürzere Variante in Listing 3. Das zweite Skript benötigte 88 Prozent mehr Laufzeit als das erste. Hängt man hinten an die Pipe ein weiteres, nutzloses »| cat« an, so dauert dies 153 Prozent länger als ein einzelnes »grep«. Zwei solche Aufrufe benötigen schon zusätzliche 239 Prozent und dreimal »cat« schlägt mit einer Zugabe von gesalzenen 322 Prozent zu Buche. Die letzteren Werte leiden überdurchschnittlich an den begrenzten Ressourcen auf dem Testrechner und lasten ihn vollständig aus. Dennoch zeigt das durchaus praxisnahe Beispiel, dass sich zusätzliche Prozesse erheblich im Schleifenrumpf auswirken.
| Listing 2: Suche mit »cat« |
|---|
01 #!/bin/bash 02 find $HOME/Artikel/2009/1* -type f | 03 while read i 04 do 05 cat "$i" | grep bashing > /dev/null 06 done |
| Listing 3: Suche ohne »cat« |
|---|
01 #!/bin/bash 02 find $HOME/Artikel/2009/1* -type f | 03 while read i 04 do 05 grep bashing "$i" > /dev/null 06 done |
Nutzloses »cat«?
Welche Daseinsberechtigung hat das Kommando dann überhaupt? Here-Dokumente wie in Listing 4 sind ein elegantes Mittel, um Konfigurationsdateien oder kurze Texte direkt in Skripte einzubetten. Bis zum vereinbarten Endzeichen darf der Programmierer Texte direkt in den Quelltext schreiben. Ihn übernimmt die Bash inklusive Zeilenumbrüchen und expaniert dort sogar Variablen (Zeile 6, funktioniert nur ab der Bash 4, bei früheren Versionen muss der Programmierer auf die Großschreibung der Namen verzichten und entfernt das Caret). Wer die wenig beachtete Variante »<<-« wie in Zeile 5 verwendet, darf seinen Input sogar per Tabulator einrücken, um den Qeulltext schöner zu gestalten – die Bash entfernt die Zeichen dann später.
| Listing 4: Eingerückte Here-Dokumente |
|---|
01 #!/bin/bash
02
03 for i in anna berta carola
04 do
05 cat <<- EOF | mail -s Einladung "$i@example.com"
06 Liebe ${i^},
07
08 kommst Du zu meinem Geburtstag? Ich freue mich!
09
10 XOX
11 EOF
12 done
|
Flucht in die Röhre
Zum Schluss noch ein Beispiel, bei der »cat« zumindest als Workaround dienen darf: Einige Programme verhalten sich unterschiedlich, je nachdem, ob sie ihre Ausgabe in eine weitere Pipe-Stufe oder in ein Pseudoterminal entlassen. Der Aufruf von »ls« unterscheidet sich von »ls | cat«. Im Fall von »ls« aktiviert der Programmierer die zeilenweise Ausgabe zwar mit der Option »-1«, aber bei manch anderem Tool fehlt diese Option. So färbt »grep« im neuen Ubuntu 9.10 per Default die Treffer rot ein, aber nur am Ende einer Pipe [4]. Das eine oder andere Skript kommt damit nicht klar, aber eine weitere Verarbeitungsstufe per »cat« würde das Problem zumindest temporär lösen.
| Infos |
|---|
| [1] Neo-Tastaturlayout:[http://www.neo-layout.org]
[2] Skript für Tastenaufkleber: [https://svn.neo-layout.org/grafik/xmodmap2tastenaufkleber/] [3] Nils Magnus, “Bash Bashing 1: Leerzeichen”, Linux-Magazin 10/09, S. 108 [4] Heike Jurzik, “Zu Befehl: Datenfluss”, Linux-User 07/07, S.94 |





