Open Source im professionellen Einsatz

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.

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
Befehle in »createHDR.sh«

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.

Eine Basis finden

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:
Parameterexpansion

 

Zeichen

Bedeutung

Erklärung

#

Einfaches Löschen von vorne

»x="aabc"«: das führende »a«
entfernt »${x#a}«

##

Gieriges Löschen von vorne

»x="aabc"«: alle führenden »a«
entfernt »${x##a}«

%

Löschen von hinten

»x="abcc"«: das letzte »c« entfernt
»${x%c}«

%%

Gieriges Löschen von hinten

»x="abcc"«: alle »c« von hinten
entfernt »${x%%c}«

/

Ändern eines Textes (erstes Vorkommen)

»x="abcabc"«: »${x/b/x}« erzeugt
»axcabc«

//

Ändern eines Textes (alle Vorkommen)

»x="abcabc"«: »${x//b/x}« erzeugt
»axcaxc«

^

Erstes Zeichen in Großbuchstaben umwandeln (Bash 4)

»x="abcabc"« verwandelt »${x^a}« in
»Abcabc«; »${x^}« wandelt einen beliebigen
ersten Buchstaben um

^^

Alle Zeichen in Großbuchstaben umwandeln (Bash 4)

»x="abcabc"« verwandelt »${x^^b}« in
»aBcaBc«; »${x^^}« wandelt den kompletten
String um

,

Erstes Zeichen in Kleinbuchstaben umwandeln (Bash 4)

»x="ABCABC"« verwandelt »${x,A}« in
»aBCABC«; »${x,}« wandelt einen beliebigen
ersten Buchstaben um

,,

Alle Zeichen in Kleinbuchstaben umwandeln (Bash 4)

»x="ABCABC"« verwandelt »${x,,B}« in
»AbCAbC«; »${x,,}« wandelt den kompletten
String um

Auf Echo verzichten

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.

Diesen Artikel als PDF kaufen

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