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.
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.
|
|
|
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.