Aufzucht und Hege von Shell-Scripts: Eingaben prüfen
Jede Eingabe ist böse?
Ja, eigentlich schon. Auf jeden Fall immer dann, wenn sie aus einer Quelle kommt, die Ihr nicht vollständig unter Kontrolle habt (Eingabe kann dabei direkter Input auf der Kommandozeile, Daten aus einer Datei, die Ausgabe eines Kommandos, ... sein). Die Eingabe kann leer sein, sie kann einen unerwarteten Wert haben (z. B. Buchstaben statt eines erwarteten numerischen Werts), sie kann numerische Überläufe verursachen oder ein Format haben, das unerwartet interpretiert wird (Zahlen mit führender Null werden von einigen Kommandos z. B. als Oktalwerte angesehen, bei denen Werte wie 09 einen Fehler verursachen), sie kann Kontrollzeichen beinhalten, ...
Oberster Grundsatz sollte also sein, dass alle Eingaben aus nicht vertrauenswürdigen Quellen vor ihrer Benutzung geprüft werden. Dazu bieten sich z. B. folgende Werkzeuge an (kleine Auswahl, es gibt - fast - unendlich viele Möglichkeiten). Näheres zu den Kommandos findet Ihr wo? Bingo: in den Manual-Pages!
- test: Ein Shell-Builtin, mit dem man Pfadnamen, leere oder nicht leere Variablen,
Werte von numerischen und alphanumerischen Variablen prüfen kann. Es gibt 2 Geschmacksrichtungen für die Syntax:
if test -f "$VAR"; then ...
if [ -f "$VAR" ]; then ...
Welche davon Ihr bevorzugt, ist eigentlich egal. Ihr solltet nur bei einer Schreibweise bleiben. - tr: Damit kann man Zeichen oder Zeichenklassen ersetzen oder löschen. Der Befehl eignet
sich z. B., wenn man prüfen will, ob eine Variable numerisch (im Sinne von Integer) ist:
test -z "$VAR" -o -n "`echo \"$VAR\" | tr -d '[0-9]'`" && echo "ist nicht numerisch"
Folgendes läuft hier ab:- -z "$VAR": Die Variable ist leer.
- -o: Oder (jetzt von innen nach außen)
- echo \"$VAR\" | tr -d '[0-9]': Löscht alle Ziffern aus der Variablen und gibt das Ergebnis auf stdout aus. Warum hier die Anführungszeichen entwertet sind, steht in Quoting.
- "`...`": Liefert die Standardausgabe des ausgeführten Kommandos als Wert (Kommandoosubstitution, siehe Manual der Shell).
- -n "`...`": Das Ergebnis ist nicht leer - die Variable enthält also noch andere Zeichen als Ziffern - folglich ist sie kein (positiver) Integer.
- ... && echo ...: Der echo wird nur dann ausgeführt, wenn das vorhergehende Kommando den Returnwert "0" geliefert hat, wenn also in diesem Fall entweder die Variable leer war oder nicht nur Ziffern enthielt.
test -z "$VAR" -o -n "${VAR//[0-9]/}" && echo "ist nicht numerisch" - cut: Schnippelt Felder oder Zeichen aus einer Eingabe heraus. Das ist nützlich, wenn man
bestimmte Teile einer Eingabe untersuchen will (in der Bash z. T. mit ${VAR:Beginn:Laenge}
möglich). Wenn Ihr z. B. wissen wollt, ob in einer Eingabe, die durch Semikolon getrennte Felder enthält, das 2. Feld
kein "A" ist, dann tut Ihr so:
test "`echo \"$VAR\" | cut -f2 -d';'`" = 'A' || echo "Feld2 ist kein 'A'" - expr: Dient normalerweise zum Rechnen. Das kann man aber auch prima nutzen, um z. B. für
Werte, von denen man weiß, dass sie Dezimalzahlen darstellen, störende führende Nullen zu entfernen:
echo `expr 09 + 0` ergibt eine feine "9", mit der man auch zickige Kommandos, die sonst mit Oktalzahlen rechnen wollen (und mit dem Wert baden gehen), zur Mitarbeit überreden kann. Gerade die numerischen Ausdrücke der Bash machen gern Ärger:
VAR=09; echo $((VAR * 2)) geht z. B. in die Hose. - grep: Jetzt tauchen wir ab in das Labyrinth der Regulären Ausdrücke.
Mit grep kann man Eingaben nach dem Vorkommen von regulären Ausdrücken durchsuchen, z. B.
um bestimmte Muster zu erkennen. Das folgende Beispiel prüft, ob eine Variable einen Wert hat, der mit einem
"H" beginnt und danach mindestens einen Kleinbuchstaben enthält; andere Zeichen sind nicht zugelassen.
Für einfache reguläre Ausdrücke:
echo "$VAR" | grep -q '^H[a-z][a-z]*$' && echo "Eingabe OK"
Mit erweiterten regulären Ausdrücken:
echo "$VAR" | grep -qE '^H[a-z]+$' && echo "Eingabe OK"
Ihr vermisst den test? Lest hier weiter: Wahr/Falsch - sed: Auch dieser Befehl arbeitet mit regulären Ausdrücken. Er kann ähnlich wie
grep nach regulären Ausdrücken fahnden, aber zusätzlich (neben vielen anderen Dingen)
auch gleich Ersetzungen vornehmen. Die folgenden Beispiele liefern aus einer Eingabe den ersten auftauchenden
Integer (wenn einer vorhanden ist):
Für einfache reguläre Ausdrücke:
echo "$VAR" | sed -n '/[0-9]/s/[^0-9]*\([0-9][0-9]*\).*/\1/p'
Mit erweiterten regulären Ausdrücken:
echo "$VAR" | sed -nr '/[0-9]/s/[^0-9]*([0-9]+).*/\1/p'
Ich erspare mir auch hier die Erklärung der regulären Ausdrücke, dann würde die Seite überhaupt kein Ende finden. RTFM! ("Read The F... Manual" ;-)
Lasst bei der Eingabevalidierung Eure Fantasie spielen! Wollt Ihr prüfen, ob eine Eingabe ein bekannter Benutzer oder eine vorhandene Gruppe ist? getent. Wollt Ihr wissen, ob da eine existierende Prozess-ID eingegeben wurde? ps. Ist das Dateisystem, welches ausgehängt werden soll, auch wirklich gemountet? mount oder df. Ist ein eingegebenes Datum auch ein Datum? date - die Liste lässt sich erweitern, bis das Laden dieser Seite die Geduld auch des größten Phlegmatikers erschöpft %-/

