Woche 2

# Tag 12

#
Perl-Skripts debuggen

Auch als guter (!) Programmierer müssen Sie damit rechnen, dass Ihre Programme - und seien sie noch so kurz - Fehler aufweisen. Einige davon sind einfacher Art - Syntaxfehler, Endlosschleifen oder Beanstandungen (Option `-w`). Andere hingegen sind versteckt und liefern unverständliche oder gar keine Ergebnisse.

Ein Weg, herauszufinden, was sich an jeder Stelle in Ihrem Skript abspielt, führt über `print`-Anweisungen. Mit Hilfe von `print`-Anweisungen können Sie die aktuellen Werte der Variablen kontrollieren und feststellen, ob Schleifen und Bedingungen auch ausgeführt werden. Es gibt jedoch noch einen anderen Weg, der besonders für längere Skripts empfehlenswert ist. Perl verfügt über einen Quellcode-Debugger, mit dem Sie die Ausführung eines Skripts schrittweise verfolgen und die verschiedenen Werte der Variablen während der Ausführung des Skripts ausgeben lassen können. Mit dem Debugger können Sie die Fehler in Ihrem Code schneller finden als mit `print`-Anweisungen.

In diesem Kapitel zeige ich Ihnen, wie Sie den Perl-Debugger einsetzen. Im einzelnen sehen Sie anhand eines kurzen Beispiels, wie Sie den Debugger normalerweise verwenden

- lernen Sie, wie Sie den Debugger starten und ausführen,
- verfolgen Sie die Ausführung Ihres Programms,
- gehen Sie Ihr Skript schrittweise durch,
- lernen Sie, Ihren Quelltext auf verschiedene Weisen anzeigen zu lassen,
- drucken Sie die Werte der Variablen aus und
- setzen Sie Haltepunkte, um die Ausführung an bestimmten Punkten zu unterbrechen.

## Einsatz des Debuggers: Ein einfaches Beispiel

Die Arbeitsweise des Perl-Debuggers läßt sich wahrscheinlich am besten anhand eines kleinen typischen Beispiels demonstrieren. Dazu gehen wir schrittweise durch die Ausführung eines einfachen Skripts mit Subroutinen, in diesem Falle das Namensskript aus Kapitel 6, »Bedingungen und Schleifen«, das eine Liste von Namen einliest, Sie bittet einen Suchbegriff einzugeben und die Namen zurückgibt, die dem Suchbegriff entsprechen.

Wenn Sie ein Skript bei eingeschaltetem Perl-Debugger ausführen, landen Sie direkt im Debugger und erhalten in etwa folgende Zeilen:

    % perl -d  statsfunk.pl statsdaten.txt

    Loading DB routines from perl5db.pl version 1.01
    Emacs support available.

    Enter h or 'h h' for help.

    main::(statsfunk.pl:3):       &initvars();
      DB<1>

> Wenn Sie noch nicht wissen sollten, wie Sie den Debugger starten, zerbrechen Sie sich darüber nicht den Kopf. Ich werde gleich nach diesem kleinen Exkurs darauf eingehen.

Die Schlußzeile `DB<1>` ist die Eingabeaufforderung des Debuggers. Hier geben Sie Ihre Befehle ein. Die Zahl `1` besagt, dass dies der erste Befehl ist. Befehle lassen sich durch Eingabe der Befehlsnummer wiederholen.

Die Zeile vor der Eingabeaufforderung gibt die aktuelle Codezeile an, die Perl anschließend ausführt. Der Teil zur Linken bezieht sich auf das aktuelle Paket, den Namen der Datei und die Zeilennummer. Der Teil zur Rechten ist die eigentliche Codezeile einschließlich der Kommentare zu der Zeile.

Um eine Codezeile auszuführen, können Sie den Befehl `n` oder den Befehl `s` verwenden. Der `s`-Befehl geht etwas mehr in die Tiefe, da damit auch Subroutinen ausgeführt werden. Mit dem `n`-Befehl bleiben Sie bei der schrittweisen Ausführung auf der obersten Ebene Ihres Skripts. Subroutinen werden im Hintergrund abgearbeitet. Beide Befehle lassen sich wiederholen, indem man bei jeder Eingabeaufforderung die Eingabe-taste betätigt:

    main::(statsfunk.pl:3):       &initvars();
      DB<1> s
    main::initvars(statsfunk.pl:8):           $input = "";
      DB<1>
    main::initvars(statsfunk.pl:9):           @nums = ();
      DB<1>
    main::initvars(statsfunk.pl:10):          %freq = ();
      DB<1>

In diesem Beispiel fällt auf, dass, sobald die Ausführung des Skripts in die `&initvars()`-Subroutine verzweigt (erfolgt in unserem Beispiel gleich in der ersten Zeile), die Informationen auf der linken Seite der Ausgabe den Namen dieser Subroutine widerspiegeln. So wissen Sie jederzeit, wo im Skript Sie sich befinden. Falls Sie aber die Übersicht verloren haben oder anhand der einzelnen Zeile Ihre Position im Skript nicht festlegen können, ist es möglich mit Hilfe des Befehls `l` (steht für list) weitere Codezeilen anzeigen zu lassen:

    DB<1> l
    10==>       %freq = ();      # Hash: Zahl-Haeufigkeit
    11:         $maxfreq = 0;    # hoechste Haeufigkeit
    12:         $count = 0;      # Anzahl Zahlen
    13:         $sum = 0;        # Summe
    14:         $avg = 0;        # Durchschnitt
    15:         $med = 0;        # Median
    16:         @keys = ();      # temp keys
    17:         $totalspace = 0; # gesamte Breite des Histogramms
    18      }
    19

Mit `l` lassen sich die auf die aktuelle Zeile folgenden zehn Zeilen am Bildschirm ausgeben. Durch Eingabe eines Minuszeichens können Sie auch die zehn Zeilen vor der aktuellen Zeile sichtbar machen. Durch mehrmaliges Eingeben von `l` und `-` können Sie sich den Quellcode auflisten lassen, die folgenden Zeilen und zurückliegende. Beachten Sie jedoch, dass Sie damit lediglich die Zeilen anzeigen lassen und Ihre Codezeile im Kontext sehen können - der Code selbst wird damit nicht ausgeführt.

Beim Durchschreiten des Codes können Sie die Werte aller Variablen - Skalare, Arrays, Hashes und so weiter - mit dem Befehl `x` ausgeben lassen. Im nächsten Beispiel hat die Subroutine `&getinput()` gerade eine Zeile aus der Eingabedatei eingelesen, diese Zeile in der Variablen `$input` abgelegt und anschließend das Zeichen für »Neue Zeile« aus dieser Zeile entfernt. Bei `DB<5>` wird der Wert von `$index` und die aktuellen Werte von `@nums` ausgegeben (die Array-Indizes stehen links und die eigentlichen Elemente rechts). Beachten Sie, dass die Codezeile (hier Zeile 23) angezeigt wird, bevor sie ausgeführt wird, so dass der Wert von `$input` noch nicht in `@nums` abgelegt ist.

    main::getinput(statsfunk.pl:21):          while (defined ($input = <>)) {
      DB<5> s
    main::getinput(statsfunk.pl:22):              chomp ($input);
      DB<5>
    main::getinput(statsfunk.pl:23):              $nums[$count] = $input;
      DB<5> x input
    0  5
    DB<6> x @nums
    0  1
    1  4
    2  3
    3  4
    DB<7>

Mit dem `x`-Befehl können Sie auch, wie im folgenden Beispiel zu sehen, Perl-Code ausführen, um die Anzahl der Elemente in `@raw` zu ermitteln oder das erste Element anzuzeigen:

    DB<3> x scalar(@nums)
    0  4
      DB<4> x $raw[0]
    0  1

Wenn Sie Ihr Skript mit `s` oder `n` debuggen, wird Ihnen jede Zeile bis ins kleinste Detail angezeigt - manchmal sogar ausführlicher, als Sie es benötigen. Innerhalb einer Subroutine können Sie mit dem Befehl `r` das schrittweise Debuggen der Subroutine aufheben, den Rest der Subroutine ausführen und dann dorthin zurückkehren, von wo die Subroutine aufgerufen wurde. Dies kann bei verschachtelten Subroutinen wiederum eine Subroutine sein.

Sie können mit der Eingabe von `c` den Debug-Prozess jederzeit abbrechen. Anschließend führt Perl den Rest des Skripts ohne weitere Unterbrechungen aus (es sei denn, Sie haben explizit Haltepunkte in Ihrem Skript aufgenommen, um zum Beispiel eine Eingabe einzulesen.)

Zusätzlich zu der Möglichkeit, die Ausführung Ihres Skripts zeilenweise zu verfolgen, können Sie die Ausführung mit Haltepunkten steuern. Unter einem Haltepunkt versteht man eine Markierung in einer Codezeile oder zu Beginn einer Subroutine. Mit dem Befehl `c` wird das Skript bis zu einem gesetzten Haltepunkt ausgeführt und dort unterbrochen. Am Haltepunkt können Sie dann mit `n` oder `s` den Code zeilenweise durchgehen, mit `x` die Variablen ausgeben oder mit `c` den nächsten Haltepunkt ansteuern.

Betrachten wir beispielsweise das Skript statsmenue.p1, mit dem wir gestern das stats-Skript in mehrere Subroutinen unterteilt haben. Die Subroutine `&countsum()` gibt die Anzahl und die Summe der Daten aus. Um die Summe zu berechnen, ruft sie die Subroutine `&sumnums()`auf. Sie setzen einen Haltepunkt bei der Subroutine `&sumnums()`, indem Sie den Befehl `b` gefolgt von dem Namen der Subroutine eingeben. Dann führen Sie mit `c` das Skript bis zu diesem Haltepunkt aus.

    # perl -d statsmenue.pl statsdaten.txt

    Loading DB routines from perl5db.pl version 1.01
    Emacs support available.

    Enter h or 'h h' for help.

    main::(statsmenue.pl:3): @nums = (); # array of numbers;
      DB<1> b sumnums
      DB<2> c
    Was moechten Sie ausgeben? (Beenden mit Q):
    1. eine Liste der Zahlen
    2. die Anzahl und Summe der Zahlen
    3. die kleinste und die groesste Zahl
    4. den Durchschnitts- und den Medianwert
    5. ein Diagramm, wie oft jede Zahl vorkommt

    Ihre Auswahl --> 2
    Anzahl der Zahlen: 70
    main::sumnums (statsmenue.pl:72):     my $sum = 0;
      DB<2>

Und wie, meinen Sie, sollen Sie sich die Namen all Ihrer Subroutinen merken? Kein Problem. Mit der Eingabe von `S` können Sie alle verfügbaren Subroutinen ausgeben lassen. Insbesondere `S main` wird die von Ihnen definierten Subroutinen anzeigen (wundern Sie sich jedoch nicht, wenn auch hier einige zusätzliche Perl-Routinen auftauchen). Betrachten Sie das folgende Beispiel:

    DB<7> S main
    main::BEGIN
    main::countsum
    main::getinput
    main::maxmin
    main::meanmed
    main::printdata
    main::printhist
    main::printmenu
    main::sumnums
      DB<8>

> Der `main`-Teil heißt so in Anlehnung an das `main`-Paket, in dem sich all Ihre Variablen und Subroutinen standardmäßig befinden. Auf Pakete werden wir im folgenden Kapitel 13 noch näher eingehen.

Wenn Sie einen Haltepunkt in einer bestimmten Zeile setzen wollen, können Sie mit `l` das Skript listenförmig ausgeben, um die Zeile zu ermitteln, und dann diese Zeilennummer zusammen mit dem Befehl `b` verwenden:

    DB<3> l
    43:         print "5. ein Diagramm, wie oft jede Zahl vorkommt.\n";
    44:         while () {
    45:             print "\nIhre Auswahl   --> ";
    46:             chomp($in = <STDIN>);
    47:             if ($in =~ /^\d$/ || $in =~ /^q$/i) {
    48:                 return $in;
    49              } else {
    50:                 print "Ungueltige Eingabe. 1-5 oder Q, bitte.\n";
    51              }
    DB<3>

Beachtenswert ist auch, dass der Debugger die Ausführung des Skripts zur Laufzeit verfolgen kann. Mit den Befehlen `n` und `s` können Sie zwar jede Anweisung einzeln ausführen lassen, aber manchmal möchte man die Zeilen nicht einzeln durchgehen, sondern sich trotzdem die einzelnen Anweisungen bei der Ausführung ausgeben lassen. Der Befehl `t` schaltet die Verfolgung ein und aus. Wir verfolgen hier die Subroutine `&printdata()`, wobei ein Haltepunkt an die Spitze der `foreach`-Schleife gesetzt wurde:

    DB<1> b 59
      DB<2> t
        Trace = on
      DB<2> c
    Was moechten Sie ausgeben? (Beenden mit Q):
    1. eine Liste der Zahlen
    2. die Anzahl und Summe der Zahlen
    3. die kleinste und die groesste Zahl
    4. den Durchschnitts- und den Medianwert
    5. ein Diagramm, wie oft jede Zahl vorkommt

    Ihre Auswahl --> 1

    die Zahlen:
    1 main::printdata(statsmenue.pl:59):         foreach $num (@nums) {
    DB<2> c
    main::printdata(statsmenue.pl:60):               print "$num ";
    1 main::printdata(statsmenue.pl:61):             if ($i == 10) {
    main::printdata(statsmenue.pl:64):               } else { $i++; }
    main::printdata(statsmenue.pl:59):           foreach $num (@nums) {
      DB<2>

An diesem Beispiel möchte ich Ihnen zeigen, dass bei der Verfolgung nicht nur die Ausgabe des Skripts angezeigt (man beachte die `1` am Zeilenanfang in der Mitte der Ausgabe direkt nach dem Befehl `c`), sondern auch jede Skriptzeile ausgegeben wird. Würden wir hier erneut `c` eingeben, würde die `foreach`-Schleife erneut durchlaufen, und wir erhielten die gleiche Ausgabe.

Verlassen wird der Debugger mit `q` (einfach nur `q`).

    DB<18> q
    %

Soweit - so gut. Nach diesem Exkurs sollten Sie eine ungefähre Vorstellung davon haben, wie der Debugger funktioniert. Der Rest dieses Kapitels ist den Details der spezielleren Befehle gewidmet.

## Den Debugger starten und ausführen

Der für Perl mitgelieferte Debugger wird von der Befehlszeile aus mit der Option `-d` gestartet. Während Sie Ihre Perl-Skript unter Unix oder Windows NT lediglich durch Angabe des Skriptnamens aufgerufen haben, so müssen Sie zum Debuggen Perl explizit aufrufen, gefolgt von dem Skriptnamen und den Argumenten. Wenn Sie also normalerweise ein Skript wie folgt aufrufen:

    % meinSkript.pl namen.txt

lautet der Aufruf für den Debugger wie folgt:

    % perl -d meinSkript.pl namen.txt

Wenn Sie der Meinung sind, dass Sie den Debugger recht häufig für ein bestimmtes ziemlich schwieriges Skript einsetzen werden, können Sie alternativ die Option `-d` auch in der shebang-Zeile des Skripts aufnehmen.

    #!/usr/bin/perl -wd

> Vergessen Sie nicht, die Option wieder zu entfernen, sobald Sie mit dem Debuggen fertig sind.

Um den Debugger in MacPerl einzuschalten, wählen Sie im Script-Menü den Befehl Perl Debugger, speichern Sie Ihr Skript, und führen Sie es dann wie gewohnt aus. Wenn Sie vorhaben, Ihr Skript als Droplet zu verwenden (das Dateien als Eingabe akzeptiert), dann vergessen Sie nicht das Skript als Droplet zu speichern.

Beachten Sie, dass vor Aufruf Ihres Debuggers das Skript frei von Syntaxfehlern und Warnungen sein muss. Sie müssen diese fatalen Fehler beseitigen, bevor Sie Ihr Skript debuggen können. Da diese Aufgabe Ihnen ohnehin nicht erspart bliebe, sollte die Bürde nicht zu schwer sein.

Während der Debugger läuft, können Sie jederzeit mit dem Befehl `h` Hilfe anfordern. Damit wird eine Liste der möglichen Befehle ausgegeben. Rollt Ihnen der Bildschirm zu schnell, steht Ihnen der Befehl `|h` zur Verfügung (pausiert nach jeder Seite). Hilfe gibt es auch zu jedem einzelnen Befehl (`h` plus Argument). Alle Befehle, die dem Argument entsprechen, werden dann wie folgt ausgegeben:

    DB<3> h c
    c [line|sub]    Continue; optionally inserts a one-time-only breakpoint
                    at the specified position.
    command         Execute as a perl statement in current package.
      DB<4>

Jeder Debugger-Befehl hat eine bestimmte Nummer (im obigen Beispiel hatte der Befehl `h` `c` die Nummer 3). Mit einem Ausrufezeichen und der Befehlsnummer können Sie jederzeit auf einen der vorangegangenen Befehle Bezug nehmen.

    DB<4> !3

Einen Überblick über die letzten paar Befehle erhalten Sie mit `H` und einer Zahl, der ein Minuszeichen vorangestellt wurde:

    DB<13> H -3
    13: H-3
    12: b sumnums
    11: x @nums
      DB<14>

Sie verlassen den Debugger mit `q`. Ist die Ausführung Ihres Perl-Skripts abgeschlossen, können Sie mit `R` die Ausführung erneut starten. Beachten Sie, dass `R` je nach Umgebung und Befehlszeilenargumenten, die für das Skript verwendet wurden, nicht funktioniert.

### Die Ausführung verfolgen

Verfolgen bedeutet, dass Sie jede Zeile Ihres Skripts angezeigt bekommen, während sie von Perl ausgeführt wird. Wird eine Zeile dabei mehrmals hintereinander ausgeführt, wie zum Beispiel in einer Schleife, wird jeder Durchgang von Perl angezeigt. Bei sehr komplexen Skripten ist die Ausgabe deshalb oft umfangreicher als notwendig. Aber mit Haltepunkten an bestimmten Positionen mag es zeitweise recht nützlich sein, die genaue Reihenfolge der Ausführung des Skripts zu verfolgen.

Um zwischen den Verfolgungsmodi in Ihrem Perl-Skript hin- und herzuschalten, steht Ihnen `t` zur Verfügung. Ist die Verfolgung ausgeschaltet, wird sie mit `t` eingeschaltet und umgekehrt. Die folgende Ausgabe zeigt zum Beispiel das Ergebnis einer ausgeführten Schleife mit ausgeschalteter und mit eingeschalteter Verfolgung (der Haltepunkt befindet sich in Zeile 59, einer `foreach`-Schleife).

    main::printdata(statsmenue.pl:59):         foreach $num (@nums) {
      DB<4> c
    2 main::printdata(statsmenue.pl:59):         foreach $num (@nums) {
      DB<4> t
    Trace = on
      DB<4> c
    main::printdata(statsmenue.pl:60):               print "$num ";
    2 main::printdata(statsmenue.pl:61):             if ($i == 10) {
    main::printdata(statsmenue.pl:64):               } else { $i++; }
    main::printdata(statsmenue.pl:59):           foreach $num (@nums) {

Bei eingeschalteter Verfolgung können Sie die Ausführung Ihres Skripts mitverfolgen. Eine Stapelverfolgung zeigt Ihnen, wo Sie bereits gewesen sind - im Falle von verschachtelten Subroutinen zeigt sie Ihnen die Subroutine, die die von Ihnen zur Zeit ausgeführte Subroutine aufgerufen hat, und alle, die eventuell noch darüber angeordnet sind - bis zur obersten Ebene Ihres Skripts. Die Stapelverfolgung wird mit dem Befehl `T` aktiviert:

    DB<4> T
    @ = main::sumnums() called from file 'statsmenue.pl' line 68
    $ = main::countsum() called from file 'statsmenue.pl' line 13
      DB<4>

Die Zeichen zu Beginn dieser Zeilen geben den Kontext an, in dem die Subroutine aufgerufen wurde. So zeigt die erste Zeile dieser Stapelverfolgung, dass die Subroutine `&sumnums()` (die aktuelle Routine) in einem Listenkontext (angezeigt durch das `@`- Zeichen am Zeilenanfang) von der Routine `&countsum()` aus aufgerufen wurde. Die Routine `&countsum()` wiederum wurde von dem `main`-Teil des Skripts in einem skalaren Kontext aufgerufen (angezeigt durch das `$` am Zeilenanfang).

### Schritt für Schritt durch das Skript

Um den Code Ihres Skripts schrittweise zu durchwandern, müssen Sie einen der Befehle `s` oder `n` verwenden. Mit der Eingabetaste wiederholen Sie Ihre vorige Eingabe (`s` oder `n`). Bei jedem Schritt zeigt Perl die Codezeile an, die es als nächstes ausführen wird (nicht die aktuell ausgeführte Codezeile):

    DB<1> s
    main::getinput(statsmenue.pl:29):                $nums[$count] = $_;
      DB<1>

Der Unterschied zwischen `s` und `n` liegt darin, dass der Befehl `s` bei der Überwachung in die Ausführung untergeordneter Subroutinen verzweigt, während `n` diese Subroutinen zwar ausführt, jedoch bei der schrittweisen Überwachung auf der gleichen Ebene bleibt.

Um die Ausführung einer Subroutine in Einzelschritten abzubrechen, den Rest der aktuellen Subroutine auszuführen und zu der Anweisung zurückzukehren, die die Subroutine ursprünglich aufgerufen hat, verwenden Sie den Befehl `r`.

Um die Einzelschrittausführung für den gesamten Code aufzuheben, verwenden Sie den Befehl `c`.

### Den Quelltext auflisten

Sie können den aktuell ausgeführten Quelltext mit Zeilennummern ausgeben lassen, um sich den Kontext zu der aktuellen Codezeile anzeigen zu lassen oder um nach einer speziellen Zeile zu suchen, bei der ein Haltepunkt gesetzt werden soll.

Die nächsten zehn Zeilen des Codes erscheinen bei Eingabe des Befehls `l`:

    DB<15> l
    79==>           $sum += $num;
    80          }
    81:         return $sum;
    82      }
    83
    84  # kleinste und groesste Zahl ausgeben
    85  sub maxmin {
    86:         print "Kleinste Zahl: $nums[0]\n";
    87:         print "Groesste Zahl: $nums[$#nums]\n\n";
    88      }
      DB<15>

Weitere Aufrufe von `l` werden die jeweils folgenden zehn Zeilen anzeigen. Sie können `l` aber auch zusammen mit einer Zeilennummer verwenden, um genau diese Zeile anzuzeigen, oder mit einem Zeilenbereich (zum Beispiel `1-4`), um speziell diese Zeilen auszugeben, oder mit dem Namen einer Subroutine, um die ersten zehn Zeilen dieser Subroutine aufzulisten.

Um in der Anzeige einige Zeilen zurückzuwandern, gibt es den Befehl -. Wie schon bei dem Befehl`l` können Sie mit mehrmaliger Eingabe von `-` auch weiter zurückwandern.

Mit dem Befehl `w` erhalten Sie einen Ausschnitt um die aktuelle Zeile (oder die spezifizierte Zeile, falls angegeben): Es werden einige Zeilen davor und einige Zeilen danach eingeblendet. Ein Pfeil (`==>`) markiert die aktuelle Zeilenposition:

    DB<3> w
    24      # Input aus Datei lesen und dann sortieren
    25      sub getinput {
    26:         my $count = 0;
    27==>       while (<>) {
    28:             chomp;
    29:             $nums[$count] = $_;
    30:             $count++;
    31:          }
    32:         @nums = sort { $a <=> $b } @nums;
    33:      }

Mit einem Mustervergleich suchen Sie nach einer bestimmten Zeile im Quelltext. `/ daten/` wird zum Beispiel nach dem ersten Vorkommen des Wortes `daten` suchen. Mit `?muster?` durchsuchen Sie die Datei rückwärts.

Einen letzten nützlichen Befehl zum Auflisten möchte ich Ihnen noch vorstellen: `S`. Damit werden Ihnen alle Subroutinen, die im Skript auftauchen, angezeigt. Die meisten dieser Subroutinen sind Perl- oder Debugger-spezifisch. `S` zusammen mit einem Paketnamen (zum Beispiel `main`) wird jedoch nur die Subroutinen in dem Paket ausgeben:

    DB<20> S main
    main::BEGIN
    main::get_muster
    main::namen_lesen
    main::suche_muster

Morgen werde ich Ihnen mehr zu den Paketen erzählen.

### Variablen ausgeben

In dem Quelltext zu Ihrem Skript herumzublättern ist nicht nur schön und praktisch, es hilft Ihnen auch, herauszufinden, was in Perl abläuft, wenn Ihr Skript ausgeführt wird. Die folgenden Befehle dienen dazu, die Werte der Variablen auszugeben:

`X` gibt alle Variablen in dem aktuellen Paket aus. Da viele dieser Variablen Perl- spezifisch sind (einschließlich spezieller Variablen wie `@_`, `$_` und `$1`), kann die Liste ziemlich lang werden. `X` zusammen mit dem Namen einer Variablen gibt alle Variablen aus, die mit dem Suchnamen übereinstimmen. Beachten Sie, dass Sie nur den Namen selbst angeben und nicht die Präfixe wie `$`, `@` oder `%`. `X foo` gibt die Werte aller Variablen aus, deren Namen `foo` lautet (`$foo`, `@foo` und `%foo`).

Der Befehl `V` wird verwendet wie `X`. Zusätzlich kann jedoch ein optionaler Paketname angegeben werden, um die Variablen dieses Pakets auszugeben. Diese Eigenschaft ist allerdings erst von Belang, wenn Sie mit Paketen arbeiten. Der Vollständigkeit halber möchte ich Sie hier jedoch erwähnen.

Der Befehl `X` birgt das Problem, dass lokale Variablen innerhalb von Subroutinen offensichtlich nicht erkannt werden. Um die Werte von lokalen Variablen auszugeben oder kleinere Perl-Fragmente auszuführen, an deren Ergebnis Sie interessiert sind, verwenden Sie den `x`-Befehl:

    DB<3> x $input
    0  'Dante Alighieri'

Wenn Sie Arrays oder Hashes ausgeben, wird der Inhalt des Arrays oder des Hash angezeigt. Die Ausgabe von `X` und `V` ist etwas einfacher zu lesen als die von `x`, besonders im Falle von Hashes. Und so sieht ein Hash bei der Verwendung von `X` aus:

    DB<4> X %names
    %names = (
       'Adams' => 'Douglas'
       'Alexander' => 'Lloyd'
       'Alighieri' => 'Dante'
       'Asimov' => 'Isaac'
       'Barker' => 'Clive'
       'Bradbury' => 'Ray'
       'Bronte' => 'Emily'
    )

### Haltepunkte setzen

Das Setzen von Haltepunkten innerhalb eines Skripts erlaubt es Ihnen, das Skript normal auszuführen und an bestimmten Punkten (in der Regel dort, wo alles anfängt, falsch zu laufen) anzuhalten. Sie können beliebig viele Haltepunkte in Ihrem Skript setzen und dann mit den Befehlen zur Einzelschrittausführung oder zur Variablenausgabe das Problem einkreisen. Nehmen Sie mit `c` die Ausführung des Codes nach dem Haltepunkt wieder auf.

Sie setzen einen Haltepunkt mit dem Befehl `b`. Wird der Befehl ergänzt um den Namen einer Subroutine, befindet sich der Haltepunkt an der ersten Anweisung innerhalb dieser Subroutine. Der Befehl `b` zusammen mit einer Zeilennummer setzt den Haltepunkt in genau dieser Zeile. Ohne Argumente wird mit `b` ein Haltepunkt in der aktuellen Zeile gesetzt. Im Quellcode-Listing erscheinen Haltepunkte als ein kleingeschriebenes `b` (in unserem Beispiel in Zeile 33):

    DB<19> w 33
    30      }
    31
    32      sub suche_muster {
    33:b        my $key = $_[0];
    34:         my $gefunden = 0;
    35:         foreach $ln (sort keys %namen) {
    36==>           if ($ln =~ /$key/o || $namen{$ln} =~ /$key/o) {
    37:                 print "$ln, $namen{$ln}\n";
    38:                 $gefunden = 1;
    39              }

Der Befehl `L` dient dazu, alle Haltepunkte, die Sie gesetzt haben, auf einmal auszugeben:

    DB<22> L
    namessub.pl:
     9:         my @raw = ();  # raw Liste von Namen
       break if (1)
     33:        my $key = $_[0];
       break if (1)

Gelöscht wird ein Haltepunkt, indem die Zeilennummer oder der Subroutinenname zusammen mit dem Befehl `d` verwendet wird. Mit `D` werden alle gesetzten Haltepunkte auf einmal gelöscht.

    DB<22> d 9
    DB<23> L
    namessub.pl:
     33:        my $key = $_[0];
       break if (1)

### Weitere Befehle

Bis jetzt habe ich Ihnen vornehmlich die Befehle vorgestellt, die Ihnen den Einstieg in die Arbeit mit dem Debugger erleichtern und die Sie wahrscheinlich am häufigsten anwenden. Neben den hier vorgestellten Befehlen können Sie mit Perl aber auch noch bedingte Haltepunkte setzen, den Wert der Variablen ändern, Aktionen in bestimmten Zeilen ausführen und mehr oder weniger komplette Perl-Skripts eingeben und deren Ausführung interaktiv verfolgen. Sobald Sie mit dem Perl-Debugger etwas vertrauter sind, werden Sie den Befehl `h` garantiert öfter verwenden und, wenn nötig, die perldebug-Manpage zu Rate ziehen.

### Zu guter Letzt

Der Debugger wird Ihnen helfen, Probleme in Ihrem Code zu lokalisieren. Die fleißige Verwendung der Option `-w` ermöglicht es, viele Probleme zu verhindern, bevor das Skript überhaupt gestartet wird (oder bevor man den Debugger überhaupt konsultieren muss, um herauszufinden, was falsch läuft). Machen Sie es sich also zur Gewohnheit, wann immer möglich, `-w` zu verwenden.

## Vertiefung

In diesem Kapitel habe ich Ihnen die wichtigsten Debugger-Befehle vorgestellt. Wenn Sie so richtig in die Arbeit mit dem Debugger einsteigen, sollten Sie in der perldebug- Manpage oder der Online-Hilfe nachschauen, welche weiteren Befehle und Optionen Ihnen zur Verfügung stehen.

### Der Einsatz verschiedener Debugger

In Perl können Sie das Verhalten des Debuggers anpassen. Sie können aber auch ein ganz anderes Debugger-System verwenden. Der Schalter `-d` zusammen mit einem Doppelpunkt und dem Namen des Moduls bindet das Modul als neuen Debugger ein. So können Sie zum Beispiel mit dem Modul `Devel::DProf` von CPAN Laufzeitprofile Ihrer Perl-Skripten erstellen (testen, wie lange Ihre Subroutinen dauern, um herauszufinden, wo Ihr Code noch effizienter sein könnte). Sobald Sie das Modul installiert haben, können Sie es wie folgt aufrufen:

    % perl -d:DProf dasSkript.pl

Die Datei `perl5db.pl` enthält den Code für den Debugger. Sie können diese Datei kopieren und Ihren Ansprüchen gemäß modifizieren. Weitere Informationen finden Sie in der perldebug-Manpage oder den Dokumentationen zu dem `DProf`-Modul.

### Perl interaktiv

Sie können den Debugger nutzen, um Perl in einer Art interaktivem Modus auszuführen. So können Sie Befehle testen und deren Ausgabe direkt verfolgen. Sie benötigen dafür nicht einmal ein richtiges Skript. Und so sieht ein einfacher Befehl aus, mit dem Sie den Debugger ohne ein Skript zum Debuggen laden:

    % perl -d -e1

> Um genau zu sein, Sie haben Perl damit ein Skript zur Ausführung übergeben. Dieses Skript ist jedoch nur ein Zeichen lang: `1`. Mit der Option `-e` werden Perl-Skripts direkt von der Befehlszeile ausgeführt. Dieses Thema schneiden wir noch einmal am Ende des Buches in Kapitel 20 an.

### Häufige Fallen und Fragen

In den letzten zwei Wochen habe ich mich bemüht, Sie auf häufige Fehler hinzuweisen, die fast allen Perl-Anfängern (und auch erfahrenen Programmierern) immer wieder unterlaufen. In der perltraps-Manpage finden Sie ebenfalls eine Liste der häufigsten Fallen und darüber hinaus noch vieles mehr. Schon ein kurzes Studieren dieser Seite wird Ihnen viele interessante Hinweise liefern, wenn es darum geht, Probleme bei schwierigem Code zu lösen.

Die Perl-Dokumentation umfaßt außerdem einen umfangreichen Satz an FAQ- Dateien (häufig gestellte Fragen). Bevor Sie sich vor Verzweiflung über ein bestimmtes Problem die Haare raufen, sehen Sie lieber einmal in den FAQs nach. Beginnen Sie dabei mit der perlfaq-Manpage, und lassen Sie sich von dort aus leiten.

## Zusammenfassung

Heute haben wir zwar nicht besonders viel über Perl selbst erfahren, dafür aber um so mehr über den dazugehörigen Befehlszeilen-Debugger. Sie wissen jetzt, wie Sie den Debugger aufrufen und starten, wie Sie jede Zeile Ihres Skripts einzeln ausführen, den Quelltext ausgeben, Haltepunkte setzen, die Ausführung verfolgen und Informationen über verschiedene Teile Ihres Skripts während seiner Ausführung einholen.

Mit dem Debugger gibt es nur wenige Probleme, die Ihnen verborgen bleiben. Es lassen sich Probleme damit schneller feststellen als mit `print`-Anweisungen.

## Fragen und Antworten

Frage:
Ich verwende emacs. Gibt es eine Möglichkeit, den Perl-Debugger zusammen mit emacs zu verwenden?

Antwort:
Aber klar. In der Datei cperl-mode.el finden Sie massenweise Material darüber, wie man Perl in den emacs einbindet. Diese Datei gehört zum Standardumfang von Perl und befindet sich in dem Verzeichnis emacs.

Frage:
Ich bin eher an Debugger mit grafischer Oberfläche gewöhnt. All diese Befehle auf Befehlszeilenebene nerven mich. Gibt es für Perl auch visuelle Debugger?

Antwort:
Wenn Sie den ActiveState-Port von Perl für Windows verwenden, steht Ihnen über ActiveState ein phantastischer visueller Perl-Debugger zur Verfügung. Weitere Informationen finden Sie unter `http://www.activestate.com`.

## Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.

### Quiz

1. Wozu dient der Debugger? Warum ist er nützlicher als zum Beispiel `-w` oder `print`?
2. Wie starten Sie den Perl-Debugger?
3. Nennen Sie drei Möglichkeiten, um Codefragmente im Debugger auszugeben.
4. Wie können Sie die Werte von Variablen im Debugger ausgeben?
5. Worin unterscheidet sich die Verfolgung von der Einzelschrittausführung des Codes?
6. Inwiefern unterscheiden sich die Befehle `X` und `V` zum Ausgeben der Variablen?

### Übungen

Geben Sie folgendes Skript ein:

    #!/usr/bin/perl -w

    my @foo = (2,5,3,7,4,3,4,3,2,3,9);

    foreach $wert (0..10) {
        &multiplizieren($wert, $foo[$wert]);
    }

    sub multiplizieren {
        my ($num, $val) = @_;
        print "$num mal $val ist gleich ", $num * $val, "\n";
    }

Lassen Sie den Debugger darüber laufen und führen Sie folgende Debugger- Operationen durch:

1. Schalten Sie mit `t` die Verfolgung ein, und lassen Sie mit `c` die Ergebnisse anzeigen.
2. Geben Sie `R` ein, um das Skript erneut auszuführen. Gehen Sie mit `n` schrittweise durch das Skript. Wenn Sie sich innerhalb der `foreach`-Schleife befinden, lassen Sie die Werte für `$wert` mehrmals ausgeben.
3. Geben Sie `R` ein, um das Skript erneut auszuführen. Gehen Sie mit `s` schrittweise durch das Skript. Geben Sie innerhalb der Subroutine `&multiplizieren()` die Werte von `$num` und `$val` aus. Kehren Sie mit `r` wieder aus der Subroutine `&multiplizieren()` zurück.
4. Geben Sie `R` ein, um das Skript erneut auszuführen. Setzen Sie mit dem Befehl `b` in der Subroutine `&multiplizieren()`einen Haltepunkt. Lassen Sie den Haltepunkt mit `L` anzeigen. Führen Sie mit `c` das Programm bis zum Haltepunkt aus. Löschen Sie mit `d` den Haltepunkt wieder.
5. FEHLERSUCHE: Gehen Sie mit dem Debugger in folgendem Skript auf Fehlersuche:

           #!/usr/bin/perl -w

       @foo = (2,5,3,7,4,3,4,3,2,9);

       while ($i < $#foo) {
           &multiplizieren($i, $foo[$i]);
       }

       sub multiplizieren {
           my ($num, $val) = @_;
           print "$num mal $val ist gleich ", $num * $val, "\n";
       }

## Antworten

Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.

### Antworten zum Quiz

1. Der Befehl `-w` dient dazu, Syntaxfehler oder schlechten Programmierstil zu finden (oder zu vermeiden). Der Perl-Debugger unterstützt Sie bei allen anderen Problemen: wenn Arrays nicht eingerichtet oder ausgegeben werden, Werte nicht übereinstimmen oder die Ausführung des Skripts nicht der erstrebten Logik entspricht. Die gleiche Hilfestellung - allerdings in reduzierter Form - erhalten Sie mit der `print`-Anweisung, die jedoch mit mehr Aufwand verbunden ist und oft größere Schwierigkeiten hat, das Problem zu orten. Mit dem Debugger können Sie auch die Werte von Variablen ändern und beliebige Codefragmente ausführen, während das Skript gerade in Ausführung ist - all dies ist mit einer einfachen `print`-Anweisung nicht möglich.
2. Um den Perl-Debugger zu starten, geben Sie in der Befehlszeile `perl` ein, gefolgt von der Option `-d`, dem Namen Ihres Skripts und etwaiger Skriptargumente. Wenn Sie mit MacPerl arbeiten, müssen Sie den Perl-Debugger über das Menü Script aufrufen.
3. Es gibt verschiedene Möglichkeiten, Code im Debugger aufzulisten:

- `l` allein zeigt die nachfolgenden Codezeilen an
- `l` zusammen mit einer Zeilennummer zeigt genau diese Zeile an
- `l` zusammen mit einem Zeilenbereich zeigt genau diesen Bereich an
- `-` bringt eine Reihe von voranstehenden Codezeilen zur Anzeige
- `w` zeigt einige Zeilen vor und einige Zeilen nach der aktuellen Zeile an

1. Die Werte von globalen Variablen und von Paket-Variablen geben Sie mit dem Befehl `X` gefolgt vom Variablennamen (ohne die Zeichen `$`, `@` oder `%`) aus. Andere Variablen und die Ergebnisse von Perl-Ausdrücken (wie zum Beispiel `$hash{'key'}`) geben Sie mit dem Befehl `x` aus.
2. Wenn Sie beim Debuggen die Verfolgung einschalten, wird jede Codezeile bei der Ausführung angezeigt - unabhängig davon, ob die Ausführung in Einzelschritten erfolgt oder nicht. Bei der Einzelschrittausführung wird jede Zeile direkt vor der Ausführung angezeigt. Wenn Sie bei der Einzelschrittausführung den Befehl `n` verwenden, überspringen Sie die Subroutinen. Diese werden zwar ausgeführt, aber ihr Inhalt wird nicht angezeigt.
3. Der Befehl `X` gibt die Variablen für das aktuelle Paket (`main`) aus. Der Befehl `V` gibt Variablen in einem beliebigen gegebenen Paket aus (wenn Sie später mit Paketen arbeiten, wird Ihnen dieser Befehl noch sehr dienlich sein).
4. Ein Haltepunkt ist eine Markierung an einer beliebigen Stelle in Ihrem Perl-Code. Wenn Sie den Code in Ihrem Debugger ausführen, läuft Perl, bis es auf einen Haltepunkt stößt. Dort wird die Ausführung angehalten. Von hier aus können Sie den Code in Einzelschritten durchwandern, Werte von Variablen ausgeben oder bis zum nächsten Haltepunkt die Ausführung fortsetzen.

### Lösungen zu den Übungen

1. 1- Hierzu gibt es keine Antworten. Die Punkte symbolisieren die Schritte, die im Debugger durchgeführt werden sollen.
2. Es gibt zwei Fehler im Skript:

- `$i` wurde nicht initialisiert, so dass die Subroutine `&multiplizieren()` Initialisierungsfehler auslöst, wenn sie versucht, den Wert von `$num` auszugeben.
- `$i` wird nicht inkrementiert. Das bedeutet, dass `i` immer den Wert `0` behält und in einer Endlosschleife hängenbleibt.

© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH