Open Source im professionellen Einsatz

Shellskripte aus der Stümper-Liga - Folge 2: Quoting

Bash Bashing

Richtiges Zitieren will gelernt sein: Schon bei einfachen Wrapper-Skripten tappen manche Entwickler in tückische Syntaxfallen. Durch das Auslassen von Quotes riskieren sie im schlimmsten Fall Datenverlust.

In jeder Programmiersprache gibt es Sonderzeichen, die nicht für sich selbst stehen, sondern syntaktische Bedeutung haben. Ein wichtiges Sonderzeichen ist das Leerzeichen, es trennt in der Bash wie in den meisten Sprachen komplexere Zeichenfolgen mit eigener syntaktischer Bedeutung, die die Sprachdesigner Wörter nennen. Quoting ist das Verfahren, den Sonderzeichen ihre besondere Bedeutung zu nehmen, sie sozusagen auf eine Stufe mit normalen Zeichen zu stellen. Die Bash bietet dafür abgestufte Möglichkeiten.

Fehlendes Quoting ist die Ursache für fehlerhafte oder zumindest fehleranfällige Skripte. Fast alle Versionen von Open Suse etwa gehen stillschweigend davon aus, dass kein Bootskript in »/etc/init.d/boot.d« Leerzeichen im Namen trägt. Anwender schätzen auch das Fax-Paket E-Fax, das ein Shellskript zum Versenden mitliefert [1]. Es scheitert ebenfalls an Blanks in Dateinamen. In beiden Fällen liegt die Ursache in fehlenden Quotes.

Open Suse selbst mag Blanks vermeiden, aber ob das auch für jeden Drittanbieter zutrifft? Und gerade im zweiten Beispiel sollte die Software unbedingt mit allen üblichen Dateinamen umgehen können, denn es spricht nichts gegen eine Datei namens »Fax an Tante Trude.pdf«.

Das Listing 1 zeigt die Hauptschleife von »fax send«, sie iteriert über alle Dateien und sammelt die zu versendenden Dateien in der Variablen »FILES«. Schon die erste Zeile ist fehlerhaft, hier muss »"$@"« statt »$*« stehen [2]. Selbst wer dies korrigiert, scheitert in fast jeder folgenden Zeile. Höhepunkt ist Zeile 13, hier löscht der Korrekturbefehl im ungünstigsten Fall sogar ganz andere Dateien.

Listing 1: Fehlende Quotes in
»fax«

01 for f in $* ; do
02   case $f in -) FILES="$FILES -" ; continue ;; esac
03   if [ ! -r $f ] ; then
04     echo "can't read file $f" ; ERR=2 ; break 2
05   fi
06   case $f in
07   *.[0-9][0-9][0-9])
08     FILES="$FILES $f" ;; # skip image files
09    *)
10     if echo ${f}.001: $f ; x | make -r -q -f -; then
11       echo ${f}.nnn is up-to-date
12     else
13       $RM ${f}.[0-9][0-9][0-9]
14       $FAX make $OPT $f
15     fi
16     if [ -r $f.001 ] ; then
17       FILES="$FILES $f.[0-9][0-9][0-9]"
18     else       # something's wrong, catch it later
19       FILES="$FILES $f.001"
20     fi
21     ;;
22   esac
23  done

Mittel zum Zweck

Ein einzelnes Zeichen verliert seine Sonderbedeutung durch Voranstellung eines Backslash: »$foo« löst die Variable »foo« nicht auf. Der Backslash heißt auch Escape-Zeichen und ist nützlich, wenn der Entwickler einzelne Sonderzeichen an ein Programm durchreichen will:

$ echo (7+3)/2 | bc
$ echo (7+3)/2 | bc
5

Die Klammern im Beispiel sind auch Sonderzeichen, denn sie leiten eine Subshell ein. Erst durch das Escape-Zeichen sieht sie der Taschenrechner »bc« in seiner Standardeingabe. Wer mehr als ein Zeichen quotieren muss, nutzt das einfache oder das doppelte Hochkomma. Das einfache Hochkomma hat die stärkste Wirkung, es nimmt jedem Zeichen die Sonderbedeutung.

Aber genau wegen dieser Mächtigkeit nutzen Skripte Singlequotes eher selten, denn das an sich schwächere doppelte Hochkomma bietet mehr Möglichkeiten: Es nimmt nämlich einigen Sonderzeichen die Sonderbedeutung (siehe Tabelle 1). Und nur deshalb sind viele Konstrukte in der Shell überhaupt möglich. Genauso wie Doublequotes verhalten sich übrigens Here-Dokumente, die der Skript-Autor beispielsweise in einen Block von »<<EOT« und »EOT« einbettet.

Tabelle 1: Zeichen
in Doublequotes

 

Zeichen

Name

Auswirkung

$

Dollar

Variablenexpansion

 

Backslash

Entwerten

`

Backquote

Programm ausführen

!

Bang

Aufrufhistorie

Variablenreferenzen

Zuweisungen der Art »myvar=$foo« sind allgegenwärtig und auch syntaktisch richtig. Trotzdem sollten Programmierer die Syntax »myvar="$foo"« verwenden. Der Grund ist einfach: Wer Variablenreferenzen konsequent in Quotes setzt, umschifft viele syntaktische Fallstricke. Die Shell entfernt die Quotes sowieso, also dürfen Programmierer großzügig mit den Anführungszeichen umgehen. In Vergleichen zum Beispiel führt

if [ $var = "abc" ]; then

zu einem Syntaxfehler, falls »$var« leer ist. Aus DOS-Zeiten ist noch der Hack

if [ x$var = "xabc" ]; then

bekannt, dort mussten Entwickler in der BAT-Sprache so programmieren. Für die moderne Shell ist das aber nicht notwendig und unelegant. Das gilt auch für:

if [ -z ${DISPLAY:=""} ]; then

Ist die Variable »$DISPLAY« leer oder nicht gesetzt, weist ihr die Shell per »:=«-Modifier einen Leerstring zu. Anschließend prüft die Bash das Ergebnis mit der Testoption »-z«, ob es leer ist. Einfacher und verständlicher ist:

if [ -z "$DISPLAY" ]; then

Im Grunde sollten Entwickler nur auf Quotes verzichten, um zu vermeiden, leere Argumente zu erzeugen. Denn die Bash macht aus Variablenreferenzen in Quotes mindestens einen Leerstring:

cat $a $b $c
cat "$a" "$b" "$c"

Im ersten Fall gibt »cat« bis zu drei Dateien aus, im zweiten Fall erhält das Programm immer drei Parameter.

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