Aus Linux-Magazin 11/2009

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

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.

Quotes innerhalb Quotes

Zwei einfache Regeln bestimmen die Wirkung von Sonderzeichen innerhalb von Quotes: Innerhalb Singlequotes darf kein einfaches Hochkomma vorkommen, auch kein per Backslash entwertetes (siehe Abbildung 1). In doppelten Anführungszeichen ist dies indes erlaubt:

Abbildung 1: Meist entwerten vorangestellte Backslashes ein Sonderzeichen, die Bash erlaubt aber nicht jede Form von Quotes innerhalb von Quotes.

Abbildung 1: Meist entwerten vorangestellte Backslashes ein Sonderzeichen, die Bash erlaubt aber nicht jede Form von Quotes innerhalb von Quotes.

myvar="Ein Zitat: "Text ...""

Ein Backslash maskiert dabei auch andere Sonderzeichen, zum Beispiel das Dollarzeichen zum Dereferenzieren von Variablen oder einen weiteren Backslash. Das in die Bash eingebaute Kommando »printf« hilft in kritischen Fällen:

printf -v myvar %q 'Ein Zitat: "Text ..."'
echo "$myvar"

Die Formatangabe »%q« sorgt für das korrekte Quoting.

Zweite Runde

Eval ist ein Shell-internes Kommando und sehr mächtig. Es sorgt dafür, dass die Shell eine Zeile ein zweites Mal auswertet. Geschicktes Quoting macht es dabei möglich, dass der Kommando-Interpreter beispielsweise Variablen erst beim zweiten Durchlauf auswertet:

rmcmd="rm"; eval $rmcmd "$@"

Das sieht obskur aus, hat aber den großen Vorteil, dass der Programmierer während der Entwicklungs- und Testphase kritische Kommandos durch ein einfaches Umdefinieren von »rmcmd«, zum Beispiel auf »echo«, gefahrlos ausführen kann. Ohne Quotierung löst schon der erste Durchlauf die Variable auf und entfernt die Anführungszeichen. Der »rm«-Befehl sieht dann nur noch ein Argument.

Quoting kann also gelegentlich ganz schön kompliziert werden, aber das ist die Ausnahme. In aller Regel sorgt ein großzügiges Quoting für robuste Skripte und dank Syntax-Highlighting bei den meisten Editoren (siehe Abbildung 2) auch für gute Lesbarkeit, selbst wenn sich vorsichtige Programmierer darauf nicht blind verlassen. (mg)

Abbildung 2: Wer Quotes schachtelt, freut sich über Syntax-Highlighting. Aber niemand sollte sich blind darauf verlassen, wie die letzten Zeilen zeigen.

Abbildung 2: Wer Quotes schachtelt, freut sich über Syntax-Highlighting. Aber niemand sollte sich blind darauf verlassen, wie die letzten Zeilen zeigen.

Infos

[1] E-Fax mit fehlerhaftem Skript »fax«: [http://www.cce.com/efax/]

[2] Nils Magnus, “Bash Bashing: Iterieren über gefährliche Leerzeichen”, Folge 1: Linux-Magazin 10/09, S. 108

Der Autor

Bernhard Bablok betreut bei der Allianz Shared Infrastructure Services ein großes Datawarehouse mit technischen Performance-Messdaten von Mainframes bis zu Servern. Wenn er nicht Musik hört, mit dem Rad oder zu Fuß unterwegs ist, beschäftigt er sich mit Themen rund um Linux und Objektorientierung.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 2 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben