Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
Shell-Programmierung von Jürgen Wolf
Einführung, Praxis, Referenz
Buch: Shell-Programmierung

Shell-Programmierung
782 S., mit CD, 44,90 Euro
Rheinwerk Computing
ISBN 3-89842-683-1
gp Kapitel 10 Fehlersuche und Debugging
  gp 10.1 Strategien zum Vermeiden von Fehlern
    gp 10.1.1 Planen Sie Ihr Script
    gp 10.1.2 Testsystem bereitstellen
    gp 10.1.3 Ordnung ist das halbe Leben
  gp 10.2 Fehlerarten
  gp 10.3 Fehlersuche
    gp 10.3.1 Tracen mit set -x
    gp 10.3.2 DEBUG und ERR-Signal
    gp 10.3.3 Variablen und Syntax überprüfen
    gp 10.3.4 Eine Debug-Ausgabe hinzufügen
    gp 10.3.5 Debugging-Tools


Rheinwerk Computing

10.3 Fehlersuchdowntop

Wenn der Fehler aufgetreten ist, dann bietet Ihnen die Shell einige Optionen, die Ihnen bei der Fehlersuche helfen. Aber egal, welche Fehler denn nun aufgetreten sind, als Erstes sollten Sie die Fehlermeldung lesen und auch verstehen können. Plausibel, aber leider werden immer wieder Fragen gestellt, warum dies oder jenes falsch läuft, obwohl die Antwort zum Teil schon eindeutig der Fehlermeldung zu entnehmen ist. Ganz klar im Vorteil ist hier derjenige, der das Buch durchgearbeitet, die Scripts abgetippt und ausprobiert hat. Durch »Trial and error« hat derjenige eine Menge Fehler produziert, aber auch gelernt, wann welche Fehlermeldung ausgegeben wird.


Rheinwerk Computing

10.3.1 Tracen mit set -x  downtop

Der Trace-Modus (trace = verfolgen, untersuchen), den man mit set –x setzt, haben Sie schon des Öfteren in diesem Buch eingesetzt, etwa als es darum ging, zu sehen, wie das Script bzw. die Befehle ablaufen. Die Option set –x wurde bereits in Abschnitt 1.8.9 ausführlich behandelt. Trotzdem muss noch erwähnt werden, dass die Verwendung der Trace-Option nur in der aktuellen Shell und den Subshells aktiv ist. Zum Beispiel wollen Sie wissen, was beim folgenden Script passiert:

# Name: areweroot
if [ $UID = 0 ]
then
   echo "Wir sind root!"
   renice –5 $$
else
   echo "Wir sind nicht root!"
   su -c 'renice –5 $$'
fi

Wenn Sie das Script jetzt tracen wollen, genügt es nicht, einfach vor seiner Verwendung die Option –x zu setzen:

you@host > ./areweroot
+ ./areweroot
Wir sind nicht root!
Password:********
8967: Alte Priorität: 0, neue Priorität: –5

Das ist ein häufiges Missverständnis. Sie müssen die Option selbstverständlich an der entsprechenden Stelle (oder am Anfang des Scripts) setzen:

# Name: areweroot2
# Trace-Modus einschalten
set -x
if [ $UID = 0 ]
then
   echo "Wir sind root!"
   renice –5 $$
else
   echo "Wir sind nicht root!"
   su -c 'renice –5 $$'
fi

Das Script bei der Ausführung:

you@host > ./areweroot2
+ ./areweroot
++ '[' 1000 = 0 ']'
++ echo 'Wir sind nicht root!'
Wir sind nicht root!
++ su -c 'renice –5 $$'
Password:*******
9050: Alte Priorität: 0, neue Priorität: –5
you@host > su
Password:********
# ./areweroot2
++ '[' 0 = 0 ']'
++ echo 'Wir sind root!'
Wir sind root!
++ renice –5 9070
9070: Alte Priorität: 0, neue Priorität: –5

Hinweis   Alternativ bietet sich hier auch die Möglichkeit, die Option -x an das Script mit einem Shell-Aufruf zu übergeben, z. B. bash -x ./script oder ksh -x ./script, wodurch sich eine Modifikation am Script vermeiden lässt. Oder Sie verwenden die She-Bang-Zeile: #!/usr/bin/bashx.


Häufig finden Sie mehrere Pluszeichen am Anfang einer Zeile. Dies zeigt an, wie tief die Verschachtelungsebene ist, in der die entsprechende Zeile ausgeführt wird. Jedes weitere Zeichen bedeutet eine Ebene tiefer. So verwendet beispielsweise folgendes Script drei Schachtelungsebenen:

# Name: datum
# Trace-Modus einschalten
set -x
datum=`date`
echo "Heute ist $datum

Das Script bei der Ausführung:

you@host > ./datum
+./script1
+++ date
++ datum=Fr Apr  1 10:26:25 CEST 2005
++ echo 'Heute ist Fr Apr  1 10:26:25 CEST 2005'
Heute ist Fr Apr  1 10:26:25 CEST 2005

Rheinwerk Computing

10.3.2 DEBUG und ERR-Signal  downtop

Für Bash und Korn-Shell gibt es eine echte Debugging-Alternative mit dem DEBUG-Signal. Das ERR-Signal hingegen ist nur der Korn-Shell vorbehalten. Angewendet werden diese Signale genauso, wie Sie dies von den Signalen her kennen. Sie richten sich hierbei einen Handler mit trap ein.

trap 'Kommando(s)' DEBUG
# nur für die Korn-Shell
trap 'Kommando(s)' ERR

Im folgenden Beispiel haben wir ein Script, welches ständig in einer Endlosschleife läuft, aber wir sind zu blind, den Fehler zu erkennen:

# Name: debug1
val=1
while [ "$val" -le 10 ]
do
   echo "Der ${val}. Schleifendurchlauf"
   i=`expr $val + 1`
done

Jetzt wollen wir dem Script einen »Entwanzer« mit trap einbauen:

# Name: debug1
trap 'printf "$LINENO :-> " ; read line ; eval $line' DEBUG
val=1
while [ "$val" -le 10 ]
do
   echo "Der ${val}. Schleifendurchlauf"
   i=`expr $val + 1`
done

Beim Entwanzen wird gewöhnlich das Kommando eval verwendet, mit dem Sie im Script Kommandos so ausführen können, als wären diese Teil des Scripts (siehe Abschnitt 9.1).

trap 'printf "$LINENO :-> " ; read line ; eval $line' DEBUG

Mit DEBUG wird nach jedem Ausdruck ein DEBUG-Signal gesendet, welches Sie »trap(pen)« können, um eine bestimmte Aktion auszuführen. Im Beispiel wird zunächst die Zeilennummer des Scripts gefolgt von einem Prompt ausgegeben. Anschließend können Sie einen Befehl einlesen und mit eval ausführen lassen (bspw. Variablen erhöhen oder reduzieren, Datei(en) anlegen, löschen, verändern, auflisten, überprüfen etc). Das Script bei der Ausführung:

you@host > ./debug1
5 :->(ENTER)
7 :->(ENTER)
9 :->(ENTER)
Der 1. Schleifendurchlauf
10 :-> echo $val
1
7 :->(ENTER)
9 :->(ENTER)
Der 1. Schleifendurchlauf
10 :-> echo $val
1
7 :->(ENTER)
9 :->(ENTER)
Der 1. Schleifendurchlauf
10 :-> val=`expr $val + 1`
7 :-> echo $val
2
9 :->(ENTER)
Der 2. Schleifendurchlauf
10 :->(ENTER)
7 :->(ENTER)
9 :->(ENTER)
Der 2. Schleifendurchlauf
10 :-> exit

Der Fehler ist gefunden, die Variable »val« wurde nicht hochgezählt, und ein Blick auf die Zeile 10 zeigt Folgendes:

i=`expr $val + 1`

Hier haben wir »i« statt »val« verwendet. Etwas störend ist allerdings, dass nur die Zeilennummer ausgegeben wird. Bei längeren Scripts ist es schwer bzw. umständlich, die Zeilennummer parallel zum Debuggen zu behandeln. Daher macht es Sinn, wenn auch hier die entsprechende Zeile mit ausgegeben wird, die ausgeführt wird bzw. wurde. Dies ist im Grunde kein Problem, da hier die DEBUG-Signale nur in der Bash bzw. der Korn-Shell vorhanden sind, weshalb auch gleich das komplette Script in ein Array eingelesen werden kann. Für den Index verwenden Sie einfach wieder die Variable LINENO. Die Zeile eval zum Ausführen von Kommandos packen Sie einfach in eine separate Funktion, die beliebig viele Befehle aufnehmen kann, bis eben (ENTER) gedrückt wird.

# Name: debug2
# ------- DEBUG Anfang --------- #
# Die übliche eval-Funktion
debugging() {
   printf "STOP > "
   while true
   do
      read line
      [ "$line" = "" ] && break
      eval $line
      printf " > "
   done
}
typeset -i index=1
# Das komplette Script in ein Array einlesen
while read zeile[$index]
do
   index=index+1
done<$0
trap 'echo "${zeile[$LINENO]}" ; debugging' DEBUG
# ------- DEBUG Ende   --------- #
typeset -i val=1
while (( $val <= 10 ))
do
   echo "Der $val Schleifendurchlauf"
   val=val+1
done

Das Script bei der Ausführung:

you@host > ./debug2
typeset -i val=1
STOP >(ENTER)
while (( $val <= 10 ))
STOP > echo $val
1
 > val=7
 > echo $val
7
 >(ENTER)
echo "Der $val Schleifendurchlauf"
STOP >(ENTER)
Der 7 Schleifendurchlauf
val=val+1
STOP >(ENTER)
while (( $val <= 10 ))
STOP >(ENTER)
echo "Der $val Schleifendurchlauf"
STOP >(ENTER)
Der 8 Schleifendurchlauf
val=val+1
STOP >(ENTER)
while (( $val <= 10 ))
STOP >(ENTER)
echo "Der $val Schleifendurchlauf"
STOP >(ENTER)
Der 9 Schleifendurchlauf
val=val+1
STOP >exit
you@host >

Hinweis   Auch in der Bourne-Shell oder anderen Shells, die eben nicht das DEBUG-Signal unterstützen, können Sie die Funktion debugging() hinzufügen.

Nur müssen Sie diese Funktion mehrmals hinter oder/und vor den Zeilen einsetzen, von denen Sie vermuten, dass das Script nicht richtig funktioniert. Natürlich sollten Sie es nicht versäumen, dies aus Ihrem fertigen Script wieder zu entfernen.


In der Korn-Shell finden Sie des Weiteren das Signal ERR, welches Sie ebenfalls mit trap einfangen können. Allerdings handelt es sich hierbei eher um ein Pseudo-Signal, denn das Signal bezieht sich immer auf den Rückgabewert eines Kommandos. Ist dieser ungleich 0, wird das Signal ERR gesendet. Allerdings lässt sich dies nicht dort verwenden, wo bereits der Exit-Code eines Kommandos abgefragt wird (bspw. if, while ...). Auch hierzu ein simples Script, welches das Signal ERR abfängt:

# Name: debugERR
error_handling() {
   echo "Fehler: $ERRNO Zeile: $LINENO"
   printf "Beenden (j/n) : " ; read
   [ "$REPLY" = "j" ] && exit 1
}
trap 'error_handling' ERR
echo "Testen des ERR-Signals"
# Sollte dem normalen Benutzer untersagt sein
cat > /etc/profile
echo "Nach dem Testen des ERR-Signals"

Das Script bei der Ausführung:

you@host > ksh ./debugERR
Testen des ERR-Signals
Fehler: Permission denied  Zeile: 4
Beenden (j/n) : j

Hinweis   Zwar wird die Bash beim Signal ERR in den Dokumentationen nicht erwähnt, aber beim Testen hat dies auch unter der Bash zu funktionieren, nur dass es eben in der Bash nicht die Variable ERRNO gibt.



Rheinwerk Computing

10.3.3 Variablen und Syntax überprüfen  downtop

Um wirklich sicherzugehen, dass Sie nicht auf eine nicht gesetzte Variable zugreifen, können Sie set mit der Option –u verwenden. Wird hierbei auf eine nicht definierte Variable zugegriffen, wird eine entsprechende Fehlermeldung ausgegeben. Mit +u schalten Sie diese Option wieder ab.

# Name: aunboundvar
# Keine undefinierten Variablen zulassen
set –u
var1=100
echo $var1 $var2

Das Script bei der Ausführung:

you@host > ./aunboundvar
./aunboundvar: line 7: var2: unbound variable

Wollen Sie ein Script nicht ausführen, sondern nur dessen Syntax überprüfen lassen, können Sie die Option –n verwenden. Mit +n schalten Sie diese Option wieder aus.


Rheinwerk Computing

10.3.4 Eine Debug-Ausgabe hinzufügen  downtop

Die primitivste Form aller Debugging-Techniken ist gleichzeitig wohl die meisteingesetzte Variante – nicht nur in der Shell-Programmierung. Sie setzen überall dort, wo Sie einen Fehler vermuten, eine echo-Ausgabe über den Zustand der Daten (bspw. welchen Wert hat eine Variable). Gibt das Script sehr viel auf dem Bildschirm aus, kann man auch einfach hie und da mal ein einfaches read einbauen, wodurch auf einen (ENTER)-Tastendruck gewartet wird, ehe die Ausführung des Scripts weiter fortfährt, oder man kann auch die ein oder andere störende Ausgabe kurzeitig ins Datengrab /dev/null schicken. Ein einfaches Beispiel:

# Name: maskieren
nobody() {
   echo "Die Ausgabe wollen wir nicht!!!"
}
echo "Ausgabe1"
exec 1>/dev/null
nobody
exec 1>`tty`
echo "Ausgabe2"

Hier interessieren wir uns nicht für die Ausgabe der Funktion »nobody« und schicken die Standardausgabe ins Datengrab. Natürlich müssen Sie das Ganze wieder rückgängig machen. Hier erreichen wir dies mit einer Umleitung auf das aktuelle Terminal (das Kommando tty übernimmt das für uns).


Rheinwerk Computing

10.3.5 Debugging-Tools  toptop

Wer ein fertiges Debugging-Tool sucht, dem seien für die Bash der Debugger bashdb (http://bashdb.sourceforge.net/) und für die Korn-Shell kshdb ans Herz gelegt. Der Bash-Debugger ist nichts anderes als eine gepatchte Version der Bash, welche ein besseres Debugging, ebenso wie eine verbesserte Fehlerausgabe unterstützt.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

 << zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Shell-Programmierung
Shell-Programmierung
bestellen
 Buchtipps
Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-Programmierung


Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo





Copyright © Rheinwerk Verlag GmbH 2005
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern