Aufzucht und Hege von Shell-Scripts: Debuggen
Wie kann man ein Shell-Script debuggen?
Einen richtigen ausgewachsenen Shell-Debugger (mit schrittweiser Ausführung, Haltepunkten usw.) kenne ich nicht, aber die Shell kennt einige Hilfsmittel, mit denen man sich das Debugging in Scripts erleichtern kann:
- set -n: Liest alle Zeilen des Scripts, führt sie aber nicht aus. Nützlich für einen ersten Syntaxcheck. Setzt den Befehl an den Beginn des Scripts, um das Script analysieren zu lassen, in Funktionen müsst Ihr das ggf. wiederholen.
- set -x: Zeigt jedes Kommando so an, wie es ausgeführt wird. Das sollte der nächste Schritt einer Fehlersuche sein, weil die Kommandos ausgeführt werden und damit auch Variablenzuweisungen, Returncodes von Befehlen usw. aktiv werden. Lest die Ausgabe SORGFÄLTIG, vergleicht Variableninhalte und Programmausgaben mit dem, was Ihr erwartet habt - manchmal sind es nur Kleinigkeiten, die auf Fehler hinweisen.
- trap: Kann unter anderem (das andere steht in Aufräumen) dazu
benutzt werden, das Verhalten bei Fehlern zu steuern, also bei einem mit Returncode ungleich 0 beendeten Befehl
ein definiertes Kommando auszuführen (z. B. Variablen auszugeben):
trap "set" ERR
oder bei jedem Kommando einen definierten Befehl auszuführen:
trap "echo $LINENO $?" DEBUG
zeigt die aktuelle Zeilennumer und den Returncode des zuletzt ausgeführten Befehls an. Näheres steht im Bash-Manual (wo sonst?). Da das Kommando wiederholt im Script auftauchen kann, lassen sich damit gezielt kritische Passagen des Scripts bearbeiten. - echo: Klein, aber fein. Tut, was der Name sagt, gibt also alles aus, was als Argument
angegeben wurde. Sinnvoll in ein Script eingestreut, ergibt das viele Möglichkeiten, um z. B. nicht gesetzten
Variablen auf die Spur zu kommen. Eine anderer guter Einsatzzweck ist, es vor potenziell riskante Befehle zu setzen,
um Bauchklatscher zu vermeiden:
echo "rm -rf /$DIR" ist deutlich ungefährlicher als ein
rm -rf /$DIR, wenn aus irgendwelchen Gründen $DIR mal leer ist. - read: Damit kann man z. B. den Programmfluss anhalten, um in einem anderen Terminal mal
zu gucken, ob die gewünschten Verzeichnisse oder Dateien angelegt wurden. Außerdem kann man damit (nur in der bash)
eine Art Variablen-Kontrolle vornehmen:
read -p "VAR=$VAR: " VAR zeigt den aktuellen Variablenwert als Eingabeaufforderung an und erlaubt das Ändern des Werts. - exit: Debugging mit exit??? Jau, damit kann ich nämlich Scripts
schrittweise ausführen, indem ich den Befehl an passender Stelle einstreue. Besonders nützlich, wenn man in größeren
Scripts noch nicht so genau weiß, an welcher Stelle der Fehler auftritt. Man tastet sich sozusagen an die fehlerhafte
Stelle von vorn nach hinten heran; ggf. verbunden mit if-Abfragen in Abhängigkeit von Programmergebnissen oder
Variablenwerten:
test $? -eq 0 || exit beendet das Programm, wenn der vorherige Befehl einen Fehler geliefert hat. - Ausgaben umleiten: Besonders für umfangreiche Scripts ist es nützlich, wenn man die
Ausgaben und Fehlermeldungen in separate Dateien schickt, um sie später in aller Ruhe analysieren und vergleichen
zu können:
./mein_script >script.out 2>script.error. - Testdaten: Bevor Ihr Eure Scripts auf Echtdaten loslasst, bastelt Euch repräsentative Testdatensätze. Es bringt Euch überhaupt nichts, wenn Ihr Fehlersuche mit einer Datei von 100.000-Zeilen treibt - Ihr geht nur im Sturm der Fehlermeldungen unter. Sucht Euch möglichst alle vorkommenden Varianten der Eingabe raus und testet mit diesem Satz. Wenn Ihr anschließend mit den Echtdaten testet, habt Ihr meist schon mehr als 90% der potenziellen Fehler ausgeschlossen.

