Open Source im professionellen Einsatz

Shellskripte aus der Stümper-Liga - Folge 4: Sinn und Unsinn von »cat«

Bash Bashing

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.

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.

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

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 2 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook