Shellskripte aus der Stümper-Liga - Folge 5: Cat, Sed, Grep & Co. vermeiden
Bash Bashing
Es gibt kaum ein Bash-Skript, das ohne Standard-Unix-Tools auskommt. Allerdings ist die Shell inzwischen so mächtig, dass viele verzichtbar sind.
Es gibt kaum ein Bash-Skript, das ohne Standard-Unix-Tools auskommt. Allerdings ist die Shell inzwischen so mächtig, dass viele verzichtbar sind.
Wenn Fotofreunde ein Motiv mit mehreren Belichtungseinstellungen fotografieren, ergeben sich mit der richtigen Software erstaunliche Resultate: High Dynamic Range nennen das die Profis [1]. Mühsam nur, wenn es viele Bilder auf einmal zu konvertieren gibt. Linux-Anwender schreiben sich für solche wiederkehrenden Aufgaben ein Skript. Listing 1 zeigt einen Ausschnitt aus einem solchen Skript, das aus Raw-Dateien automatisch HDR-Dateien erzeugt [2].
|
Listing 1: Zu viele externe |
|---|
01 i=0
02 cat "$DIR"/pfs.hdrgen | while read LINE; do
03 echo "${FILES[$i]} $LINE" | cut -d' ' -f1,3- >> "$DIR"/pfs_updated.hdrgen
04 let "i = $i +1"
05 done
|
Der Code im Beispiel ist nicht falsch, aber kompliziert und unverständlich. Auch wenn das Beispiel extrem anmutet: Man kommt nicht immer an per Pipe verbundenen Befehlsketten vorbei. Doch oft sind solche Konstrukte überflüssig und zeugen nur von mangelnder Kenntnis der Bash-Möglichkeiten. Diese Folge des Bash Bashing zeigt ein paar Wege auf, ohne externe Befehle auszukommen.
Ein bekannter Klassiker in Skripten ist die Zuweisung »verz=$(dirname "$0"), um das Verzeichnis zu finden, in dem das Skript liegt. Dabei geht das auch einfacher mit »verz="${0%/*}"«. Geht es um den reinen Programmnamen, hilft »prog="${0##*/}"« statt des externen Aufrufs:
prog=`basename "$0"`
Das Syntaxelement in beiden Fällen heißt Parameter-Expansion. Der Wert einer Variablen - hier des nullten Positionsparameters - übernimmt die Bash dabei nicht einfach, wie er ist, sondern verändert ihn zusätzlich. Das Prozentzeichen löscht dabei den auf sich selbst folgenden Text von hinten, die Raute von vorne. Bei doppeltem Prozentzeichen oder Raute verfährt die Bash gierig und löscht den maximal möglichen String, der passt. In der einfachen Form bleibt die Bash konservativ und nimmt nur den ersten passenden Ausdruck.
Der Trick ohne externes Programm »dirname« klappt allerdings nicht immer, denn die Shellvariante gibt einen Punkt zurück, wenn ihr Argument beispielsweise »demo.sh« lautet, die Bash hingegen den kompletten Namen übrig lässt. Die Tabelle 1 gibt einen Überblick verschiedener Optionen und Funktionen der Parameter-Expansion.
|
Tabelle 1: |
||
|---|---|---|
| Zeichen |
Bedeutung |
Erklärung |
| # |
Einfaches Löschen von vorne |
»x="aabc"«: das führende »a« |
| ## |
Gieriges Löschen von vorne |
»x="aabc"«: alle führenden »a« |
| % |
Löschen von hinten |
»x="abcc"«: das letzte »c« entfernt |
| %% |
Gieriges Löschen von hinten |
»x="abcc"«: alle »c« von hinten |
| / |
Ändern eines Textes (erstes Vorkommen) |
»x="abcabc"«: »${x/b/x}« erzeugt |
| // |
Ändern eines Textes (alle Vorkommen) |
»x="abcabc"«: »${x//b/x}« erzeugt |
| ^ |
Erstes Zeichen in Großbuchstaben umwandeln (Bash 4) |
»x="abcabc"« verwandelt »${x^a}« in |
| ^^ |
Alle Zeichen in Großbuchstaben umwandeln (Bash 4) |
»x="abcabc"« verwandelt »${x^^b}« in |
| , |
Erstes Zeichen in Kleinbuchstaben umwandeln (Bash 4) |
»x="ABCABC"« verwandelt »${x,A}« in |
| ,, |
Alle Zeichen in Kleinbuchstaben umwandeln (Bash 4) |
»x="ABCABC"« verwandelt »${x,,B}« in |
Will der Programmierer den Inhalt von Shellvariablen an Programme verfüttern, dann scheint der Echo-Befehl ein probates Mittel:
zeilen=`echo $foo | grep -i muster` if [ -n "$zeilen" ]; then ... fi
In manchen Shells ist dieses Konstrukt tatsächlich notwendig. Die Bash dagegen erlaubt es, den Inhalt von Shellvariablen direkt an die Standardeingabe eines Programms zu binden:
zeilen=`grep -i muster <<< "$foo"` if [ -n "$zeilen" ]; then ... fi
Alternativ darf das Kommando auch direkt hinter dem Schlüsselwort »if« stehen, denn dort sind auch andere Befehle außer »test« oder seines häufiger benutzen Alias »[« erlaubt:
if grep -qi muster <<< "$foo"; then ... fi
Die Bedingung ist in diesem Fall abhängig vom Returncode des Kommandos, das die Shell auch in »$?« ablegt.
Alle Rezensionen aus dem Linux-Magazin
Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...