CGI und Perl
Description: Einführung in die CGI-Programmierung mit Perl im Internet. Vorkenntnisse der Programmierung sind notwendig.
Keywords: Multimedia WWW Internet Webdesign CGI Perl Programmierung

| ### Grundlagen CGI-Programmierung mit Perl von Prof. Jürgen Plate

## 2 Einführung in Perl

Das unvermeidliche erste Programm:

    #!/usr/bin/perl
    #
    # Das unvermeidliche erste Programm
    #
    print 'Hello world.';           # Ausgabe eines Textes

Fast jedes Perl-Programm beginnt unter UNIX mit der Zeile

    #!/usr/bin/perl

obwohl die Zeile von System zu System unterschiedlich aussehen kann,sagt sie dem Computer, was er bei der Ausführung zu tun hat. In diesem Fall soll er das Programm durch den Perl-Interpreter schicken.

Das #-Symbol eröffnet einen Kommentar. Alles zwischen # und dem Zeilenende wird vom Interpreter ignoriert (Ausnahme: erste Zeile). Kommentare können überall im Programm verwendet werden. Der einzige Weg um Kommentare über mehrere Zeilen ausdehnen zu können, ist die Verwendung von # in jeder Zeile.

Alles übrige sind Perl-Anweisungen, welche mit einem Strichpunkt beendet werden müssen, wie die letzte Zeile oben.

Die `print` Funktion gibt Information aus. Im obigen Fall gibt sie die Zeichenkette Hello world. aus.

### Ein Programm ausführen

Schreiben Sie das Beispielprogramm mit einem Texteditor und speichern Sie es ab. Danach muß es ausführbar gemacht werden. Beispielsweise mit dem UNIX-Kommando

    chmod u+x progname

Um das Programm zu starten, können Sie eine der folgenden Möglichkeiten wählen:

    perl progname
    ./progname
    progname

Vielleicht haben Sie Glück und das Programm wird ausgeführt. Bei der Ausführung eines Perl-Programmes (Perl-Script) wird der Text zuerst 'kompiliert' (interpretiert). Dieser Schritt erzeugt auch die Fehlermeldungen. Anschliessend wird es ausgeführt. Die eigentliche Laufzeit ist vergleichbar mit einem C-Programm. Je nach Länge des Programmes, muss mit einer gewissen 'Kompilierzeit' gerechnet werden.

Möglicherweise erscheinen aber Fehlermeldungen, oder es geschieht überhaupt nichts. Man kann beim Aufruf, den Perl-Interpreter veranlassen, Warnungen und weitere hilfreiche Meldungen auszugeben, bevor das Programm ausgeführt wird.

### 2.1 Aufruf

Beim Aufruf des Perl-Interpreters (UNIX-Version) lassen sich einige Optionen angeben (Aufruf-Beispiel: "perl -c test.pl" oder "perl -cwT test.pl").

Option | Bedeutung
-c | Das Perl-Script wird nicht ausgeführt, sondern nur auf syntaktische Richtigkeit überprüft
-d | Das Perl-Script wird im Debug-Modus ausgeführt.
-S | Das Script braucht nur als Dateiname ohne Pfad angegeben werden. Wenn es in einem der Verzeichnisse liegt, die in der path-Variablen des Betriebssystems genannt sind, findet der Perl-Interpreter das Script auch ohne Pfadangabe.
-T | Das Script wird während der Ausführung streng auf Fehlerfreiheit überprüft
-v | Gibt die Version und den genauen Stand des Perl-Interpreters aus.
-w | Bewirkt, daß der Perl-Interpreter nicht nur offensichtliche Fehlermeldungen bei Syntaxfehlern ausgibt, sondern auch Warnungen bei möglichen Fehlern und logisch zweifelhaften Anweisungsfolgen.

### 2.2 Sprachkontext

Perl setzt keine Vereinbarung von Typen, Datenstrukturen, Variablen, Funktionen wie in anderen Programmiersprachen voraus. Für komplexe Probleme ist es jedoch sinnvoll, Variablen am Programmanfnag zu definieren, vorzubesetzen und zu kommentieren. So werden viele Fehler im Vorfeld vermieden - oder schnell gefunden. Wie leicht vertippt man sich an irgendeiner Stelle im Programm bei einem Variablennamen. Dann läuft es natürlich nicht korrekt und gerade solche Fehler sind schwer zu finden. Man kann diese strenge Überprüfung auf korrekt definierte Variablen- und Unterprogrammnamen mit der Anweisung

         use strict;

im Programm erreichen. Andererseits hat man in Perl für die vielen einfachen, alltäglichen Probleme eine Programmiersprache in der man einfach und sofort sagen kann, was man tun will. Es genügt beispielsweise die Zeile

        print "Hello world!\n":

und schon wird `Hello world!` auf der Konsole ausgeben.

Der Kontext, in dem ein Literal oder eine Variable verwendet wird, wird durch die verwendeten Operatoren erzwungen. Es gibt zunächst keinen Unterschied zwischen Zahl und Zeichenkette. Paßt der Kontext, wird eine Zeichenkette als Zahl interpretiert. Zum Beispiel ergibt `"33" + "44"` die Zahl 77. Dagegen ergibt `"33" . "44"` die Zeichenkette "3344".

### 2.3 Variablen

Eine Variable ist ein Behälter für Werte, ein Behälter mit einem Namen, so daß man ihn (und seinen Inhalt) wiederfinden kann. Dabei gibt es Variablen, die nur einen Wert enthalten können, genannt Skalar, und Variablen, die mehrere Werte enthalten können, genannt Array. In Perl werden die verschiedenen Arten von Variablen durch spezielle Zeichen markiert:

Typ | Zeichen | Beispiel | bezeichnet:
Skalar | $ | $count | einzelnen Wert (Zahl oder String)
Array | @ | @namen | Liste von Werten, über numerischen Index ansprechbar
Hash | % | %eingabe | Assoziatives Array aus (String-)Wertepaaren, erster String ist Zugriffsschlüssel
Unterprogramm | & | &convert | Aufrufbarer Perl-Code
Typeglob | * | *irgendwas | Alles was "irgendwas" genannt wird

#### Skalare Variablen

Zuweisungen an Skalare erfolgen wie in den meisten Programmiersprachen mit dem "="-Operator. Einer Skalar-Variable kann dabei jeder skalare Wert zugewiesen werden (Integer, Fließpunktzahlen, Strings und sogar Referenzen auf andere Variablen oder Objekte). Es gibt verschiedene Möglichkeiten, die Werte für die Zuweisung zu erzeugen.

Will man Variablen einen ganzzahlingen Wert sedezimal oder oktal zuweisen, muß man "0xzzz" (sedezimal) bzw. "0zzz" (oktal) schreiben. Wie bei C sind Konstante mit führender Null immer oktal.

Zum Thema "undefinierte" Variablen ist noch zu sagen, daß diese nicht gleichbedeutend sind mit definierten Variablen, welche die Werte "0" oder "" (leerer String) besitzen. Perl bietet eine Funktion mit dem der "Definiert"-Status einer Variablen geprüft werden kann und hier würde eine definierte Variable des Wertes "0" oder "" nicht als "undef" erkannt werden.

Ein spezieller Kontext für skalare Variablen stellt der Boole'sche Kontext dar. Hier wird jede Variable als "true" interpretiert, deren Wert nicht "0" oder der leere String "" ist.

Beispiele für Variablen und Zuweisungen:

    $antwort = 42;                         # Integer-Wertzuweisung
    $x = 0xAFFE;                           # Sedezimalzahl
    $o = 0773;                             # Oktalzahl
    $pi= 3.14159265;                       # Gleitpunktzahl-Wertzuweisung
    $groesse = 7.678e12;                   # Desgleichen in wissenschaftlicher Notation
    $tier =  "Kamel";                      # String (das Perl-Maskottchen)
    $satz =  "Ich liebe mein $tier";       # String mit Ersetzung
    $preis = 'Dei Variable heisst $tier';  # String ohne Ersetzung
    $gesamtpreis = $anzahl * $einzelpreis; # Ausdruck
    $cwd = `ls /etc`;                      # Kommandoausgabe
    $exit = system("ls /etc");             # numerischer Kommandostatus

Uninitialisierte Variablen werden bei Bedarf automatisch erzeugt. Nach dem Prinzip der minimalen Überraschung werden sie mit Nullwerten gefüllt, entweder ""0"" oder "0". Je nach Verwendung werden Variablen automatisch als Strings, Zahlen oder boolsche Werte interpretiert. Dabei gelten Nullwerte als "false", alle anderen Werte hingegen als "true" (auch z. B. "00").
Verschiedene Operatoren erwarten bestimmte Werte als Parameter. Man nennt dies "sie stellen einen 'skalaren Kontext' zur Verfügung". Dies kann auch genauer spezifiziert werden als numerischer Kontext, String-Kontext oder boolscher Kontext. Innerhalb sinnvoller Grenzen wandelt Perl die Daten automatisch in den erforderlichen Kontext um. Zum Beispiel bei:

    $var = '123';
    print "$var + 1", "\n";

Der ursprüngliche Wert von $var ist ein String, er wird jedoch zu einer Zahl umgewandelt um 1 dazu zu addieren. Beim anschließenden "print" wird der Inhalt von $var für die Ausgabe wieder in einen String umgewandelt. Das Zeilenende "\n" ist ebenfalls im String-Kontext, da es aber bereits ein String ist, entfällt die Umwandlung.

#### Vordefinierte Skalare

Die folgende Liste ist nicht vollständig, sondern enthält nur einige wichtige vordefinierte Skalare.

Skalar | Erklärung | Beispiel
--- | --- | ---
$_ | Die bekannteste vordefinierte Variable in Perl. Sie enthält den jeweils aktuellen Wert (Eingabe, Suche, usw.). |
    @Zahlen = (1..10);
    for(@Zahlen)
    { print $_, "\n"; }
$. | Enthält die aktuelle Zeilennummer der zuletzt eingelesenen Datei. |
    open(DATEI, "<readme.txt");
    while(<DATEI>)
      { print $_; }
    print $., " Zeilen gelesen";
    close(DATEI);
$/ | Enthält den eingestellten Eingabeseparator. Kann geändert werden - auch mehrere Zeichen sind erlaubt. |
    $/ = "ENDE";
    # statt \n ist nun "ENDE" das Zeilenende
    $Eingabe = <STDIN>;
    print $Eingabe;
$] | Enthält die Versionsnummer des verwendeten Perl-Interpreters. |
    print $];
$! | Enthält die aktuelle Fehlermeldung oder Fehlernummer, sofern ein Fehler aufgetreten ist. |
    open(DATEI, "<nixda.txt") || print $!;
$0 | Enthält den Dateinamen des Perl-Scripts, das gerade ausgeführt wird. |
    print $0;
$$ | Enthält die Prozeß-ID des Perl-Scripts, das gerade ausgeführt wird. |
    $tempdatei = "Data." . $$;
$^T | Enthält den genauen Zeitpunkt (Millisekunden), zu dem das Script gestartet wurde. |
    ...   # langes Programm
    $Start = $^T; $Jetzt = time;
    print "Laufzeit: ", $Jetzt - $Start, " ms\n";
$& | Enthält nach Anwenden eines regulären Ausdrucks den Wert, auf den das Suchmuster paßte. |
    $Satz = "Katz und Maus";
    $Satz =~ /\bund\b.*/;
    print $&;
$+ | Enthält nach Anwenden eines regulären Ausdrucks mit Klammern den Inhalt der Klammer, die mit dem letzten Suchmuster übereinstimmte. |
    $Satz = "Katz und Maus";
    $Satz =~ /(\bund\b).*/;
    print $+;
$1, $2, ... | Enthält nach Anwenden eines regulären Ausdrucks mit Klammern die Werte der Klammern 1, 2 usw., auf die das in der jeweiligen Klammer definierte Suchmuster paßte. |
    $Satz = "Hund und Katz und Maus";
    $Satz =~ /(\bund\b).*/;
    print "$1 $2";

#### Quoting

Wie in der UNIX Shell kann man auch in Perl verschiedene Quoting-Mechanismen benutzen:

- doppelte Anführungszeichen (double quotes, "...") fügen Variablen-Inhalte in den String ein und lösen Backslash-Ausdrücke auf. Das folgende Beispiel gibt "Hallo Welt" gefolgt von Zeilenwechsel aus:

      $txt = "Hallo";
      print "$txt Welt\n";

- einfache Anführungszeichen (single quotes, '...') unterdrücken jegliche Interpretation von Variablen und Backslash. Das folgende Beispiel gibt "$txt Welt\n" aus:

      $txt = "Hallo";
      print '$txt Welt\n';

- rückwärts gerichtete Anführungszeichen (backquotes, `...`) führen ein externes Programm aus und liefern seine Ausgabe zurück, so daß alle Zeilen der Programmausgabe als einzelner String zur Verfügung stehen.

Die Anführungszeichen sind nicht die einzigen Quotes, alternativ kann man verwenden:

Customary | Generic | Meaning | Interpolates
--- | --- | --- | ---
`''` | `q//` | Literal | No
`""` | `qq//` | Literal | Yes
```` | `qx//` | Command | Yes
`()` | `qw//` | Word list | No
`//` | `m//` | Pattern match | Yes
`s///` | `s///` | Substitution | Yes
`y///` | `tr///` | Translation | No

`//` darf durch andere Zeichen ersetzt werden (keine Buchstaben, Ziffern oder Whitespace), z. B.: `!!` oder `~~` oder `||` oder `{}`. Beispiel: Mehrzeiligen String zuweisen:

    $STRING = qq~
    Erste Zeile
    Zweite Zeile
    Dritte Zeile
    ~;

#### Here-Documente

Wie in der UNIX-Shell kann der gleiche Zweck wie im obigen Beispiel auch durch ein Here-Dokument erreicht werden:

    print <<EOF;
    Die Antwort ist $answer.
    EOF

bziehungsweise

    print <<"EOF";
    Die Antwort ist $answer.
    EOF

Achtung:

- kein Whitespace nach `<<`
- `;` nach der Ende-Markierung

#### Listen

Unter Listen versteht man eine Menge verschiedener Werten oder Datentypen, die logisch zusammengehören. So bilden z. B. die Werte ("Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag") eine Liste. Bei der Definition von Listen gilt:

- die einzelnen Werte sind durch das Komma voneinander getrennt
- Listen sind meistens von Klammern umschlossen

In Perl gibt es die Möglichkeit eine Liste entweder als Array oder als Hash zu speichern. Auf die Unterschiede zwischen beiden wird in den nächsten Kapiteln eingegangen. Allgemein gibt es noch zwei wichtige Punkte zu Listen.

Der Unterschied zwischen skalarem Kontext und Listenkontext ist für einige Funktionen und Operatoren von Bedeutung, da diese entweder Skalare oder Listen erwarten oder unterschiedliches Verhalten je nach Kontext haben. Das folgende Beispiel verdeutlicht dies:

    ($a,$b,$c) = ("eins", "zwei", "drei");
    # jedes Element links erhält den entsprechenden Wert rechts

    $d = ("eins", "zwei", "drei");
    # nur das letzte Elemente rechts wird $d zugewiesen

    ($e) = ("eins", "zwei", "drei");
    # nur das erste Elemente rechts wird $e zugewiesen

Listen, egal ob Klammern, Arrays oder Hashes (siehe später), interpretieren die Werte ihrer Elemente immer im Listenkontext. Das bedeutet, es gibt zwar keine Einschränkung dazu was ein Listenwert sein kann - z. B. kann eine Liste auch aus mehreren Arrays bestehen - die Werte dieser "geschachtelten" Arrays jedoch werden so behandelt, als wären sie Elemente der übergeordneten Liste. Listen innerhalb von Listen verlieren demnach ihre "Eigenständigkeit".

#### Operationen und Zuweisungen

Perl verwendet alle gebräuchlichen C-Operatoren.

    $a = 1 + 2;   # Addiere 1 und 2 und speichere Resultat in $a
    $a = 3 - 4;   # Subtrahiere 4 von 3 ...
    $a = 5 * 6;   # Multipiziere 5 und 6
    $a = 7 / 8;   # Dividiere 7 durch 8 ($a = 0.875)
    $a = 9 ** 10; # Neun hoch zehn
    $a = 5 % 2;	  # 5 MOD 2
    ++$a;         # Inkrement $a, Rückgabe von $a
    $a++;         # Rückgabe von $a, Inkrement $a
    --$a;         # Dekrement $a, Rückgabe von $a
    $a--;         # Rückgabevon $a, Dekrement $a

Für Strings gibt es unter anderem:

    $a = $b . $c;  # Konkateniere $b und $c
    $a = $b x $c;  # Füge $b $c-mal zusammen

Verkürzte Zuweisungen:

    $a += $b;   # $a = $a + $b
    $a -= $b;   # $a = $a - $b
    $a .= $b;   # $a = $a . $b

Die folgenden Programmzeilen sollen Äpfel and Birnen ausgeben:

    $a = 'Äpfel';
    $b = 'Birnen';
    print $a . ' and ' . $b;

Es wäre schöner, nur einen String im print statement zu verwenden, aber mit

    print '$a and $b';

erhalten wir $a and $b, was nicht unserem Wunsch entspricht. Die Verwendung von doppelten Anführungszeichen veranlasst Perl die Variablen aufzulösen:

    print "$a and $b";

Daneben werden auch Spezialzeichen, wie \n (Newline) und \t (Tabulator) aufgelöst.

#### Arrays

Bestimmte Arten von Variablen können mehrere Werte aufnehmen die logisch miteinander verknüpft sind: Arrays und Hashes. Sie verhalten sich in vieler Hinsicht wie Skalare, denn sie werden bei Bedarf leer erzeugt. Wenn ihnen ein Wert zugewiesen wird, so stellen der rechten Seite der Zuweisung einen sogenannten "Listen-Kontext" zur Verfügung.

Ein Array ist eine geordnete Liste von Skalaren, auf die über die Position des Skalars in der Liste (Index) zugegriffen wird. Diese Liste kann Zahlen, Strings oder beides enthalten (sie kann sogar Referenzen auf andere Arrays enthalten, was den Aufbau multidimensionaler Arrays ermöglicht). Die Indexzählung beginnt immer bei 0. Um einem Array einen Listenwert zuzuweisen, gruppiert man einfach die Variablen mit Klammern zusammen:

    @feld = ();                         # leeres Feld
    @Farben = (rot, gelb, gruen, blau); # vier Zeichenketten
    @krimskrams = (1, 2, 3, "hallo");   # gemischt belegt

Man kann ein Feld auch in einem Listen-Kontext benutzt, wie z. B. auf der rechten Seite einer Listen-Zuweisung. Auf diese Weise kann man Skalarvariablen aus einem Array mit Werten belegen, z. B.:

    ($oben, $mitte, $unten) = @farben;

Wie in C, so werden auch in Perl Arrays null-basiert indiziert. Der Zugriff auf einzelne Komponenten eines Arrays erfolgt durch einen in eckige Klammern eingeschlossene Index-Ausdruck, zum Beispiel:

    $farben[0] = rot;
    $farben[1] = gelb;
    $farben[2] = gruen;

Der Index-Ausdruck kann jede beliebige Operation sein, die ein Integer als Ergebnis hat, also auch z. B. "$farbe[$i]" oder "$farbe[$i - $j]". Beachten Sie, daß bei Zugriff auf einzelne Komponenten der Klammeraffe wieder dem Dollarzeichen weicht. "@farbe" würde nämlich das ganze Feld referieren, so erhält man mit "print @farbe" als Ausgabe "rotgelbgruen". Der letzte Index eines Feldes wird durch die skalare Variable "$#feldname". Im obigen Beispiel liefert $#farbe den Wert 2. Einen recht verblüffenden Effekt von Perl liefert die folgende Zuweisung:

    $z = @farben;

Der Perl-Interpreter liefert den einzigen sinnvollen skalaren Kontext für ein Array, nämlich die Anzahl der Elemente. $z ist also gleich "$#farben + 1". Der skalare Kontext kann auch erzwungen werden:

    $z = scalar(@farben);

Desweiteren kann in Perl der Index auch von hinten abgezählt werden. Im obigen Beispiel hätte die Anweisung:

    print $farben[-2];

"gruen" ausgegeben. Das letzte Element könnte man also auch als `$farben[-1]` schreiben. Bei der Zuweisung können auch wieder Listen auf der rechten Seite auftauchen:

    @colors = (schwarz, weiss, @farben, cyan, magenta);

Das Feld hat nun die Werte

    $colors[0] = schwarz
    $colors[1] = weiss
    $colors[2] = rot
    $colors[3] = gelb
    $colors[4] = gruen
    $colors[5] = blau
    $colors[6] = cyan
    $colors[7] = magenta

Da Arrays geordnet sind, kann man daüber auch die Stackoperationen "push" und "pop" ausführen. Perl betrachtet das Ende eines Arrays als Spitze eines Stacks, mit "push @array $skalar" wir ein Wert an ein Array angehängt. Man kann so ein Array sukzessive aufbauen. Mit dem internen Kommando "sort" kann eine Liste sortiert werden. Aber auch ohne "push" sind Arrays beliebig erweiterbar.

    @feld = (null, eins, zwei);
    $feld[$#feld+3] = fuenf;

Nun gilt:

    feld[0] = null
    feld[1] = eins
    feld[2] = zwei
    feld[3] ist undefiniert
    feld[4] ist undefiniert
    feld[5] = fuenf

Beispiel: Eine Liste sortiert ausgeben, Doubletten entfernen

    @list = sort(@list);
    $old = "";
    foreach $element (@list)
      {
      if ($element ne $old)
        {
        print $element, "\n";
        }
      $old = $element;
      }

#### Array-Funktionen

Die folgenden Tabelle zeigt die wichtigsten Array-Funktionen. Auf einige wird bei den Zeichenkettenfunktionen nochmals eingegangen.

Funktion | Beispiel | Bedeutung
--- | --- | ---
grep | @gefunden = grep(Muster, Liste); | Sucht alle Elemente der Liste, die den regulären Ausdruck "Muster" erfüllen.
splice | Liste = splice(@array,skip,anz,Neueliste); | Baut "anz" Elemente von "Neueliste" ab der Stelle "skip" in das Array ein und übergibt die so entstandene Liste ans Ergebnis.
join | $skalar = join(Ausdruck,@array); | Fügt alle Elemente der Liste zu einem Skalar zusammen. Zwischen die Elemente wird das Ergebnis des Ausdrucks (Kontext: Zeichenkette) gesetzt.
split | Liste = split(Muster,$skalar); | Teilt das Skalar an den durch das Muster identifizierten Stellen auf und bildet aus den einzelnen Elementen eine Liste.
chop | chop(Liste); $skalar = chop(Liste); | Löscht das letzte Zeichen eines jeden Listenelements. Gibt das gelöschte Zeichen des letzten Listenelements zurück.
push | push(@array,Liste); | Fügt die Liste am Ende vom Array an.
pop | $skalar = pop(@array); | Holt ein Element aus der Liste.
sort | @sortiert = sort(Liste); | Sortiert eine Liste aufsteigend.
reverse | @sortiert = reverse(Liste); | Sortiert eine Liste absteigend.
shift | $skalar = shift(Liste); | Liefert das erste Element einer Liste.
unshift | $anz = unshift(@array,Liste); | Fügt die Liste am Anfang des Arrays ein. Das Ergebnis liefert die Anzahl der Elemente des neuen Arrays.
@array = () | @array = (); | Entfernt alle Elemente eines Arrays.

Arrays können auch verwendet werden, um gleichzeitig mehrere Zuordnungen von skalaren Variablen zu machen:

    ($a, $b) = ($c, $d);             # Aequvalent von $a = $c; $b = $d;

    ($a, $b) = @array;               # $a und $b sind die ersten
                                     # beiden Elemente von @array.

    ($a, @somearray) = @array;       # $a ist das erste Element von @array
                                     # @somearray sind die übrigen

    (@somearray, $a) = @array;       # @somearray ist @array und
                                     # $a ist nicht definiert.

Weil in Perl immer der Kontext eine Rolle spielt, ist es nicht erstaunlich, daß die folgenden Ausdrücke alle unterschiedliche Resultate erzeugen:

    print @food;              #
    print "@food";            # mit doppelten Anführungszeichen
    print "@food.''";         # in skalarem Kontext (Konkatenation)

Übrigens funktioniert auch eine Zuweisung im skalaren Kontext, z. B.:

    $a = "@array";

#### Hashes

Ein "Hash" oder "assiziatives Array" ist eine Liste von Skalaren, auf die nicht über einen Indes, sondern über einen eindeutigen Schlüsselstring zugegriffen wird. Ein Hash speichert pro Listenelement zwei Werte: einen Namen und einen Wert. Werte innerhalb eines Hashes werden also über den zugehörigen Namen "assoziiert". Hashes sind intern über sogenannte Hashtabellen implementiert (daher der Name) und deshalb sehr effizient. Beim Zuweisen von Werten an Elemente des Hashes muß ebenfalls der Schlüssel angegeben werden. Der Name eines Hashes wird durch das Prozentzeichen eingeleitet. Elemente innerhalb eines Hashes werden über Dollarzeichen angesprochen, wobei der Name in geschweifte Klammern gesetzt wird. Beispiel:

    %Daten = ("Name","Meier",
              "Vorname","Hans",
              "Ort","Berlin"
             );
    print $Daten{'Name'}, " wohnt in ", $Daten{'Ort'};

Dies ist eine Form, den Hash mit Inhalten zu füllen. Eine weitere Möglichkeit ist (ab Perl 5):

    %Daten = ("Name" => "Meier",
              "Vorname" => "Hans",
              "Ort" => "Berlin"
             );

Oder auch durch:

    @Array = ("Name","Meier","Vorname","Hans","Ort","Berlin");
    %Daten = @Array;

Ein Hash kann in ein normales Array umgewandelt werden, indem er einfach dem Array zugewiesen wird. Umgekehrt kann ein Array in einen Hash umgewandelt werden. Idealerweise hat das Array eine gerade Anzahl von Elementen:

    @info = %hash;          # @info ist ein normales Array.
    $info[5];               # Das 5. Element von @info
    %newhash = @info;       # %newhash ist ein assoziatives
                            # Array. Es ist der gleiche wie %hash

Für Hashes gelten im Übrigen die gleichen Regeln wie für gewöhnliche Listen. Mit der Funktion "keys" erhält man beispielsweise eine Liste der Schlüssel.

    foreach $key (keys(%hash))     # oder foreach $key (sort (keys(%hash)))
      {
      print $key, ": ", $hash{$key}, "\n";
      }

Weitere Funktionen für Hashes finden Sie in der folgenden Tabelle.

#### Funktionen für Hashes

Funktion | Beispiel | Bedeutung
--- | --- | ---
keys | @array = keys(%hash); | Liefert eine Liste der Indizes des Hash.
values | @array = values(%hash); | Liefert eine Liste der Inhalte des Hash.
each | @array = each(%hash); | Liefert das nächste Wertepaar in das Array.
delete | $skalar = delete($hash($key)); | Löscht den durch $key identifizierten Hash-Eintrag.
exists | if (exists($hash($key))) ... | Liefert "wahr", wenn ein Eintrag zum Key vorhanden ist (auch wenn der Key undefiniert ist).
%hash = () | %hash = (); | Lösch alle Einträge eines Hash.
undef %hash | undef %hash; | Entfernt den Hash vollständig - auch die Variable ist nun verschwunden.

#### Vordefinierte Listen und Hashes:

Die folgende Auflistung ist nicht vollständig, sondern enthält nur einige wichtige vordefinierte Listen- und Hash-Variablen.

Liste /Hash | Erläuterung | Beispiel
--- | --- | ---
@_ | Parameter, die beim Aufruf eines Unterprogramms übergeben wurden. Innerhalb des Unterprogramms sind die übergebenen Parameter mit `$_[0]` (erster Parameter), `$_[1]` (zweiter Parameter) usw. ansprechbar. |
    &Sprich("We are the champion!");
    sub Sprich
      {
      print $_[0];
      }
@ARGV | Parameter, die beim Aufruf des Perl-Scripts auf der Kommandozeile übergeben wurden. |
    for($i=0; $i <= $#ARGV; $i++)
      {
      print $ARGV[$i], "\n";
      }
%ENV | Enthält die Umgebungsvariablen, wie Sie dem Perl-Interpreter bekannt sind. Das nebenstehende Beispiel gibt die Elemente der in diesem Hash gespeicherten Daten aus. |
    print "User $ENV{'USER'} mit ";
    print "Homedirectory $ENV{'HOME'}\n";

Eine besondere Rolle unter den oben aufgelisteten Hashes spielen die Umgebungsvariablen. UNIX kennt das Konzept von Umgebungsvariablen, welche es erlauben, Informationen über das System an Programme weiterzugeben. Zum Beispiel wird in der Variablen USER den Namen des eingeloggten Benutzers gespeichert und in der Variablen HOME dessen Arbeitsverzeichnis. Perl stellt diese Variablen in dem assoziativen Array %ENV zur Verfügung. Die Schlüsselwörter dieses Arrays sind die Namen der Umgebungsvariablen. Somit wird das folgende Programmstück die aktuellen Werte aller Umgebungsvariablen ausgeben:

    for(%ENV)
      {
      print $_, "\n";
      }

### 2.4 Operatoren

Perl kennt auch die üblichen arithmetischen Operatoren (Addition "+", Subtraktion "-", Modulo "%", Exponentation "**" usw.). Für Strings gibt es noch den Append-Operator "." und den Repeat-Operator "x". Die Zuweisungsoperatoren (einschließlich Autoincrement und Autodekrement) entsprechen denen von C, ebenso die logischen Operatoren, die Perl außerdem, dem Sprachkonzept entsprechend, als Worte anbietet ("and", "not", ...). Ebenfalls von C stammen die Vergleichsoperatoren.

#### Arithmetik und Vergleiche

Vorrang und Assoziativität verhalten sich wie folgende Tabelle zeigt, von höchstem Vorrang abwärts. Gelb unterlegte Felder werden weiter unten näher erläutert:

Art | Assoziativität | Operatoren
--- | --- | ---
Ausdruck | links | Ausdrücke und Listenoperatoren
Pfeiloperator (infix Dereferenzierer) | links | ->
Autoinkrement / -dekrement | ohne | ++ --
Potenzierung | rechts | **
grafische unäre Operatoren | rechts | ! ~ \ unäre + -
bindende Operatoren | links | =~ !~
Multiplikative Operatoren | links | * / % x
Additive Operatoren | links | + - .
Bitweises Links- und Rechtsschieben | links | << >>
benannte unäre Operatoren | ohne | z. B. scalar, int, sin
Relationale Operatoren | ohne | < > <= >= lt gt le ge
Gleichheitsoperatoren | ohne | == != <=> eq ne cmp
Bitweises NOT, Komplement | links | ! nbsp; ~
Bitweises UND | links | &
Bitweises OR, XOR | links | | ^
logische Operatoren | links | &&
logische Operatoren | links | ||
Bereichsoperator | ohne | ..
Bedingter Operator | rechts | ?:
Zuweisungsoperatoren | rechts | = += -= *= /= usw.
Kommaoperatoren | links | , =>
logische Operatoren | rechts | not
logische Operatoren | links | and
logische Operatoren | links | or
logische Operatoren | links | xor

bindende Operatoren`=~ !~`:

- werden benötigt für Pattern Matching und Ersetzen
- das linke Argument enthält die Variable deren Inhalt gematcht, ersetzt oder übersetzt werden soll,
- das rechte Argument enthält das Suchmuster, die Ersetzungs- oder Übersetzungsvorschrift,
- bei !~ wird der Rückgabewert des Matching negiert,

Gleichheitsoperatoren `== != <=> eq ne cmp`:
Von Interesse hier ist vor allem "`<=>`" (für numerische Werte) bzw. "`cmp`" (für Zeichenketten):

- Rückgabe -1 wenn links < rechts
- Rückgabe 0 wenn links = rechts
- Rückgabe 1 wenn links > rechts

logische Operatoren: `&& ||`
Wie in C werden sie von links nach rechts bearbeitet und brechen ab, sobald die Bedingung erfüllt ist.

bedingter Operator: `?:`
Syntax:

    TEST_AUSDR ? IF_TRUE->AUSDR : IF_FALSE->AUSDR;

Ist der `TEST_AUSDR` wahr, wird nur der `IF_TRUE` Ausdruck ausgewertet und das Ergebnis dieses Ausdruckes bestimmt den Wert des gesamten Ausdrucks, ist `TEST_AUSDR` falsch, wird `IF_FALSE` ausgewertet.

#### Zeichenketten-Operatoren

- Zeichenkettenverknüpfung: Punkt .
- Zeichenkettenwiederholung: x

      print "A"."h" x 10, "!\n";
      # erzeugt die Ausgabe "Ahhhhhhhhhh!"

### 2.5 Kontrollstrukturen

Es gibt den üblichen Satz der bekannten Kontrollstrukturen, sowie ein paar Perl-Spezialitäten. Ähnlich wie bei C werden die Anweisungen unterhalb eine Kontrollstruktur in geschweiften Klammern zusammengefaßt. Im Gegensatz zu C sind geschweifte Klammern für Zweige auch bei nur um einer einzigen Anweisung nötig.

#### Bedingte Anweisungen

    if (Bedingung) { ... }

    if (Bedingung)  { ... }
    else { ... }

    if (Bedingung) { ... }
    elsif (Bedingung)  { ... }
    elsif (Bedingung)  { ... }
       ...
    else { ... }

    unless (Bedingung) { ... }

    unless (Bedingung) { ... }
    else { ... }

Beispiel:

    for($i = 1; $i <= 5; $i ++)
      {
      if    ($i < 3)  { print "($i) 1 oder 2\n" }
      elsif ($i == 4)    { print "($i) gleich 4\n" }
      elsif ($i > 4)  { print "($i) kann nur 5 sein\n" }
      else               { print "($i) keine der anderen Bedingungen erfuellt\n" }
      }

Anmerkungen:
Bedingung werden durch Vergleiche oder reguläre Ausdrücke dargestellt. Nach der Auswertung der Bedingungen bedeuten die leere Zeichenkette (""), "0" und 0, daß die Bedingung nicht erfüllt wurde (falsch) und alle anderen Werte, daß die Bedingung erfüllt wurde (wahr). So entspricht `(<DATEI>)` "`! EOF(DATEI)`". "unless" bedeutet "wenn nicht", ist also ein "if" mit negierter Bedingung. "`if (/zeichenkette/)`" bedeutet "wenn die Zeichenkette im gelesenen Datensatz enthalten ist".

#### Schleifenkonstrukte

    while (Bedingung) { ... };     # nichtabweisende Wiederholung

    until (Bedingung) { ... };     # dito, Bedingung gegenüber while negiert

    do { ... } while (Bedingung);  # nichtabweisende Wiederholung

    do { ... } until (Bedingung);  # nichtabweisende Wiederholung, Bed. negiert

    for (Anfangsbedingung; Endebedingung; Veränderung) { ... }

    foreach $var (@list) { ... };

Beispiel:

    $i = 1;
    print "Wurzeln von 1 bis 10...\n\n";
    while ($i <= 10)
      {
      print "Die Wurzel von ", $i, " ist ", sqrt($i), "\n";
      $i++;
      }
    $i = 1;
    print "Quadratzahlen von 1 bis 10...\n\n";
    until ($i > 10)
      {
      print "Das Quadrat von ", $i, " ist ", $i * $i, "\n";
      $i++;
      }

Schleifen mit "for"/"foreach" sind vor allem für Fälle, in denen Anfangswert und Endwert einer Schleife von vorneherein feststehen. "for (@Liste)" durchläuft die Schleife für jedes Element der Liste (jeweils aktuelles Listenelement als $_). Zum Beispiel:

    for((1,2,3,4,5,6,7,8,9,10)) { print $_."\n" }
    foreach((1,2,3,4,5,6,7,8,9,10)) { print $_."\n" }
    foreach(1..10) { print $_."\n" }
    foreach $nr (1..10) { print $nr."\n" }

    for($i=0;  $i <= 10;  $i++)
      {
      print "Das Quadrat von ", $i, " ist ", $i * $i, "\n";
      }

    @Alphabet = (A..Z);
    for(@Alphabet)
      {
      print $_, " ist der ", $i+1, ". Buchstabe im Alphabet\n";
      $i++;
      }

#### Sprungbefehle

- `exit`
  sofortiges Beenden des Perl-Programms. Hinter "exit" kann ein Return-Code (Wert 0 für O.K., Wert > 0 als Fehlercode) stehen.
- `goto Ziel`
  springe sofort zu dem mit "Ziel:" markierten Befehl.
- `redo`
  Der aktuelle Schleifendurchgang wird abgebrochen. Der nächste Schleifendurchgang wird gestartet, jedoch ohne daß die Schleifenbedingung neu bewertet wird (z. B. kein Hochzählen eines Schleifenzählers bei "for").
  redo innerhalb des Schleifenblocks verhindert, daß der continue-Block ausgeführt wird.
- `next`
  Der aktuelle Schleifendurchgang wird abgebrochen. Der nächste Schleifendurchgang wird gestartet, und die Schleifenbedingung wird dabei ebenfalls ausgewertet.
- `last`
  bricht eine Schleife sofort vollständig ab.
- `continue`
  leitet einen eigenen Anweisungsblock ein, genauso wie eine Schleife. Innerhalb dieses Anweisungsblocks (continue { ... }) können Sie beliebige Anweisungen notieren. Ein continue-Block unmittelbar hinter einem Schleifenblock wird bei jedem Schleifendurchlauf ausgeführt.

#### Verkürzte Strukturen

Kommt nach if, unless, while oder do nur eine Anweisung sind bei Perl ja trotzdem geschweifte Klammern (also ein Block) nötig. Dafür kennt Perl jedoch eine Verkürzung durch Umstellen. Statt

    if ($a > $b) { $max = $a; }

kann man schreiben:

    $max = $a if ($a > $b);

Das gilt analog auch für die anderen Programmstrukturen. Mit der do-Anweisung lassen sich zudem noch Anweisungsblöcke wie ein einziger Ausdruck betrachten und so auch in diesem Kontext einsetzen. Das Ergebnis eines solchen do-Blocks ist der Wert der letzten Anweisung des Blocks.

    do { $a = 2; $b = 5; $a * $b }

verhält sich wie ein Ausdruck, der den Wert 10 liefert. Man kann den obigen do-Block beispielsweise in einen Vergleich einsetzen:

    if(do { $a = 2; $b = 5; $a * $b } == 1)
     { print "It works!\n"; }

Ein Ausdruck hinter do wird als Name einer Datei mit einem Perl-Skript betrachtet und dieses im Kontext des aktuellen Programms ausgeführt. Z. B.:

    $skript = "einfuege.pl";
    do $skript;

### 2.6 Reguläre Ausdrücke

Perl beherrscht als eingebautes Feature reguläre Ausdrücke. Dabei gibt es zwei Klassen: die Mustervergleichs-Operatoren "/foo/" und die Ersetzungs-Operatoren "s/foo/bar/". In Vergleichen kann Mustervergleich (pattern matching) mit dem Operator " =~ " angefordert werden. Reguläre Ausdrücke in Perl basieren auf einem NFA (nicht-deterministischer finiter Automat), der folgendermaßen vorgeht: Er merkt sich die Stellen, an denen mehr als eine Möglichkeit zu kontrollieren ist. Stellt er beim Testen einer Variante fest, daß der Gesamtausdruck nicht mehr zutrifft, geht er zurück zum "Scheideweg" und prüft die Alternative. Erst wenn alle abgehakt sind, entscheidet der NFA, ob der Ausdruck zutrifft oder nicht. Durch dieses "Backtracking" genannte Vorgehen beherrscht Perl numerierte Rückbezüge wie `s/(Eins) (Zwei)/\2\1/g`. Hier sorgen die Klammern dafür, daß Perl sich jedes "Eins" und jedes "Zwei" merkt. Im zweiten Teil vertauscht dann `\2\1` die beiden miteinander.

Perl versucht normalerweise, den frühesten Treffer im String zu finden. Kommt aber ein Quantifizierer wie `*` ins Spiel, will der NFA soviel wie möglich finden, er wird gierig ("greedy"). Dabei ist die "Gierigkeit" stärker als die "Links-Bindung".
Will man in HTML-Code einen bestimmten Tag erwischen, heißt der erste Versuch vermutlich: `/<.*>/`. Übersetzt: "Suche beliebig viele (auch gar kein) Zeichen, umschlossen von spitzen Klammern." Was würde Perl nun in der Zeile

    <B>Wir</B> sind die <B>Champions</B>!

finden? Alles von der ersten spitzen Klammer bis zur letzen vor dem Ausrufezeichen. Da ein Quantifizierer dabei ist, gilt nicht mehr "Treffer soweit links wie möglich" (also `<B>`) sondern "Soviel wie möglich". Perls Gierigkeit läßt sich jedoch durch ein hinter `+` oder `*` gesetztes Fragezeichen beschränken. Benutzt man im obigen Beispiel `<.*?>`, wird es `<B>` finden. In solchen Fällen hilft ebenfalls: `/<[^>]+>/`. Dieser Ausdruck sucht ein <, dann etwas, was kein > ist, davon mindestens eines, schließlich ein >. Ähnlich geht man zum Beispiel vor, um Worte in Anführungszeichen zu finden: `/"[^"]+"/` erledigt diesen Job besser als `/".*"/`. Die folgende Übersicht faßt die verschiedenen Möglichkeiten zusammen.

/string/ | adressiert die nächste Zeile, die 'string' enthält (rückwärts suchen mit ?string?)
^ | steht für den Zeilenbeginn. `/^Meier/` adressiert Zeile, die mit "Meier" beginnt
$ | steht für das Zeilenende. `/Meier$/` adressiert Zeile, die mit "Meier" endet. `/^$/` adressiert die nächste Leerzeile (Achtung! Doppelbedeutung! Im Suchmuster eines reg. Ausdrucks Zeilenende, als Adresse im ed-Kommando Dateiende).
[] | definiert einen Buchstaben aus dem Bereich in []. Beginnt der Bereich mit ^, wird nach einen Zeichen gesucht, das nicht im Breich enthalten ist (Bei Dateinamen war dies das !-Zeichen). `[ABC]`: einer der Buchstaben A, B oder C `[A-Z]`: Großbuchstaben `[A-Za-z]`: alle Buchstaben `[^0-9]`: keine Ziffer
. | der Punkt steht für ein beliebiges Zeichen
* | der Stern "*" steht für eine beliebige Folge des vorhergehenden Zeichens (auch Null Zeichen!). `a*`: Leerstring oder beliebige Folge von a's `aa*`: eine beliebige Folge von a's (mindestens eines) `[a-z]*`: Leerstring oder eine beliebige Folge von Kleinbuchstaben `[a-z][a-z]*`: eine beliebige Folge von Kleinbuchstaben (mindestens einer) `.*`: jede beliebige Zeichenfolge
*? | der Stern "*" ist recht "gefräßig" (greedy), d. h. es wird versucht, maximal viele Zeichen in den reg. Ausdruck einzuschließen. Bei der Zeichenkette "aaa:bbb:ccc" würde der Ausdruck ".*:" die Zeichenkette "aaa:bbb:" finden. Durch das nachgestellte Fragezeichen wird diese Eingenschaft umgekehrt, es wird die minimal Teizeichenkette genommen - also im obigen Beispiel "`aaa:`".
+ | das Pluszeichen "+" steht für eine beliebige Folge des vorhergehenden Zeichens, jedoch mindestens eines. `a+`: ein a oder beliebige Folge von a's
+? | Mindestens einmal das vorhergehende Zeichen.
? | Nullmal oder einmal das vorhergehende Zeichen.
\ | hebt den Metazeichen-Charakter für das folgende Zeichen auf `a*` steht für Leerstring oder beliebige Folge von a's `a\*` steht für die Zeichenfolge "a*"
( ) | Reguläre Ausdrücke können mit Klammern gruppiert werden. `([A-Za-z]*)` gruppiert beispielsweise ein Wort aus beliebig vielen Buchstaben, wobei auch ein leeres Wort (0 Buchstaben) dazugehört. Soll das Wort mindestes einen Buchstaben enthalten, muß man `([A-Za-z][A-Za-z]*)` oder `([A-Za-z]+)` schreiben. Die Anwendung solcher Gruppen wird weiter unten gezeigt - es kann in den Editor-Befehlen nämlich Bezug auf die Gruppen genommen werden.
\i | Referenzieren des i-ten Klammerausdrucks im regulären Ausdruck Später können die Klammerausdrücke mit $1, $2, ... referenziert werden.

Bei Perl werden sie in Verbindung mit den folgenden Operatoren verwendet:

- `=~ m/.../`: ist enthalten,
- `!~ m/.../`: ist nicht enthalten
- `=~ s/.../.../`: Textersetzung und
- `=~ tr/././`: Zeichenersetzung

Beispiele:

    if ($value =~ m/Meier/)       # "Meier" in der Zeichenketten enthalten?
    $value =~ tr/+/ /;            # Ersetze "+" durch Leerzeichen
    $value =~ tr/A-Z/a-z/;        # Ersetze alle Grossbuchstaben durch Kleinbuchstaben
    $value =~ s/Meier/Huber/;     # Ersetze einmal "Meier" durch "Huber"
    $value =~ s/Meier/Huber/g;    # Ersetze alle "Meier" durch "Huber"

Der folgende Ausdruck ersetzt alle Zeichenfolgen, die auf das Muster "%-Zeichen, gefolgt von zwei Sedezimalziffern" passen, durch den Buchstaben, der den ASCII-Wert der Hexzahl hat:

    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

Wie Sie sehen, kann der Ersetzungsteil auch einen Perl-Ausdruck enthalten. Für die Zeichenklassen hat Perl noch einige Abkürzungen:

#### Zeichen und Klassen

. | ein beliebiges Zeichen außer Newline; paßt im Singleline-Mode auf das \n-Zeichen
x | ein x
Wort | "Wort"
[xyz] | Zeichenklasse: trifft x, y oder z
[a-z] | Zeichenklasse: paßt auf jedes Zeichen zwischen a und z
(Wort) | eingefangenes "Wort", wird in $i gespeichert; mit \i im Ausdruck verwendbar
(?:Wort) | gruppieren, aber nicht speichern
x(?=y) | x wenn y folgt; Klammern speichern nicht
x(?!y) | x wenn kein y folgt. Klammern speichern nicht
\w | Wortzeichen (A..Za..z0..9_)
\w+ | ganzes Wort (Wortzeichen mit folgendem Leer- oder Satzzeichen)
\W | Nicht-Wortzeichen: Satzzeichen, Leerzeichen und so weiter
\s | Leerraum: Leerzeichen, Tabulator, Newline, Return
\S | alles, was kein Leerraum ist: Buchstaben, Ziffern et cetera
\b | Wortgrenze, wie in vi \< und \>
\d | Ziffer

#### Quantifizierer

gierig | genügsam | Beschreibung
--- | --- | ---
* | *? | passt auf null oder mehr Zeichen
+ | +? | passt auf ein oder mehr Zeichen
? | ?? | passt auf null oder ein Zeichen
{n} | {n}? | passt auf genau n Zeichen
{n,} | {n,}? | passt auf n oder mehr Zeichen
{n,m} | {n,m}? | passt auf mindestens n bis höchstens m Zeichen

Das Verhalten der Quantifikatoren hängt nun davon ab, ob sie gierig oder genügsam sind. Gierige Quantifikatoren passen immer auf soviele Zeichen wie möglich (ohne dabei das übergeordnete Ziel, dass nämlich der ganze reguläre Ausdruck passen soll, aus dem Auge zu verlieren). Analog passen genügsame Quantifikatoren immer auf sowenige Zeichen wie möglich. Quantifikatoren binden sehr stark und beziehen sich immer auf das Muster direkt vor ihnen. Wird nicht geklammert, ist dieses Muster ein Einzelzeichenmuster Beim Einsatz von Quantifikatoren muss man darauf achten, dass nicht nur solche vorkommen, die auch auf kein Zeichen passen können (* und ?). Ein regulärer Ausdruck, in dem alle Teilausdrücke von solchen Quantifikatoren gebunden sind, passt immer!

Schließlich gibt es noch reguläre Ausdrücke mit Alternativen, dabei dient das Pipesymbol als Trenner:

- /a|b/ findet 'a' oder 'b', entspricht also [ab]
- /aus|ohne/ findet 'aus' und 'Hausbau' aber auch 'ohne' und 'Bohne'
- /a|bc|d/ findet 'a' oder 'bc' oder 'd'
- /(a|b)(c|d)/ findet 'ac' oder 'ad' oder 'bc' oder 'bd'

#### Sonderzeichen

| | oder, auf Zeichen und Gruppen anwendbar: (ganz)|(gar nicht)
^ | Anfang der Zeile oder des Ausdrucks
$ | Ende der Zeile oder des Ausdrucks
[^x] | alles, was nicht "x" ist
\ | Backslash, nimmt Sonderzeichen die Spezialbedeutung
\U | alle Zeichen bis \E in Großbuchstaben
\L | alle Zeichen bis \E in Kleinbuchstaben
\Q | alle Sonderzeichen bis \E mit \ schützen
\E | Ende von \U, \L, \Q

Achtung: Die Metazeichen

    + - ? . * ^ $ ( ) [ ] { } | \

müssen durch ein davorstehenden Backslash (\) geschützt werden, wenn sie im Suchstring auftauchen.

Beispiel: Deutsche Umlaute HTML-gerecht ersetzen

    $Text =~ s/ä/\&auml;/g;
    $Text =~ s/ö/\&ouml;/g;
    $Text =~ s/ü/\&uuml;/g;
    $Text =~ s/Ä/\&Auml;/g;
    $Text =~ s/Ö/\&Ouml;/g;
    $Text =~ s/Ü/\&Uuml;/g;
    $Text =~ s/ß/\&szlig;/g;

Weitere einfache Beispiele:

    # Alles in Grossbuchstaben
    $line =~ tr/a-z/A-Z/;

    # Alles in Kleinbuchstaben
    $line =~ tr/A-Z/a-z/;

    # fuehrende Leerzeichen entfernen
    $line =~ s/^\s+//;

    # Leerzeichen am Ende entfernen, auch \n
    $line =~ s/\s+$//

    # Testen, ob eine Jahreszahl vierstellig ist
    # Format: nn/nn/nnnn oder nn-nn-nnnn
    # true, falls OK
    ($line =~ m/[0-9]{2}[\/|-][0-9]{2}[\/|-][0-9]{4}/)

    # Entfernt HTML-Tags aus dem Text
    $line =~ s/(<([^>]+)>)//ig;

Perls reguläre Ausdrücke können vorausschauen, ob ein String passen könnte ("lookahead"). Mit /Haus(?=bau)/ findet der Interpreter "Haus" nur dann, wenn "bau" folgt. Schließt sich an "Haus" jedoch "herr" an, trifft der Ausdruck nicht mehr zu. Das Ganze darf man auch verneinen: "Finde jedes 'Haus', aber nur, wenn dem kein 'bau' folgt": /Haus(?!bau)/.

Für numerierte Rückbezüge kennt Perl zwei Schreibweisen: Die Variablen $1, $2, $3 und so weiter enthalten jeweils den Wert des in der ersten, zweiten, dritten ... einfangenden Klammer gefundenen Musters. Sie lassen sich irgendwo im Programm verwenden. \1, \2 sind dagegen Bestandteil der RegEx-Maschine. In ihnen steht ebenfalls die in den einfangenden Klammern gefundene Wert. Die Anzahl der Rückbezüge ist in beiden Fällen unbegrenzt. Vorsicht ist jedoch geboten: Aufgrund der Art und Weise, wie die RegEx-Maschine den Ausdruck interpoliert und kompiliert, gibt es durchaus einen Unterschied zwischen $1 und \1. Empfohlen wird, innerhalb des Ausdrucks nur den Rückbezug mittels \1 zu verwenden, da Variablen wie $1 erst später ausgewertet werden und beim ersten Interpretieren eines Ausdrucks möglicherweise noch gar keinen Wert besitzen. Neben $1, ... belegt Perl bei jeder RegEx-Auswertung einige Spezialvariablen neu: In $& findet sich immer der letzte gültige Treffer, in $’ alles, was vor ihm und in $‘ das, was nach ihm lag.

Perl erlaubt in seinen regulären Ausdrücken Ausdrücke und Funktionen, die einen korrekten String ergeben, wenn man den Modifizierer e benutzt. Er sorgt dafür, daß die RegEx-Maschine alle Variablen interpoliert, den Ausdruck übersetzt, \1 usw. belegt und den Ersetzungsteil evaluiert. Beispiel:

    ...
    $system =~ s/Betriebssystem/&os/eg;
    ...

    sub os
      {
      $string = `uname -a`;
      return $1 if ($string =~ /(^\w+)/)
      }

Hier wird "Betriebssystem" durch den Rückgabewert von os() ersetzt.

#### Übersicht: Operatoren, Modifizierer und Spezialvariablen

m// | suche
s/// | suche und ersetze
s### | suche und ersetze, aber verwende # als Trenner
//g | jedes Vorkommen finden
//i | Groß- und Kleinschreibung ignorieren
//m | Multiline-Mode, "^" und "$" passen auf logische Zeilenanfänge und -enden; "." matched kein Newline
//s | Singleline-Mode, "^" und "$" erkennen Anfang/Ende des gesamten Strings; "." matched Newline
//sm | kombiniert: Logische Zeilen plus Newline
//x | Kommentare und Leerzeichen im Suchen-Teil erlaubt
s///e | kann im Ersetzungsteil einen Ausdruck erst evaluieren und dann ersetzen; erlaubt ist alles, was einen ersetzungsfähigen Ausdruck ergibt
$1 | Wert der ersten einfangenden Klammer; nur im Ersetzungsteil verwenden
\1 | Rückbezug auf die erste einfangende Klammer
$& | letzter gefundener Treffer
$' | Text nach dem Treffer
$` | Text vor dem Treffer
$+ | Treffer der letzten einfangenden Klammer
$/ | Zeilentrenner (Input Record Separator)

#### Beispiele:

Abschließend zwei Beispiele für häufig verwendete reguläre Ausdrücke. Der erste Ausdruck beschreibt eine E-Mail-Adresse (zumindest oberflächlich), um testen zu können, ob sich ein Sendeversuch überhaupt lohnt:

    ([\w\-\.]+)@([\w\-\.]+)

Die beiden Zeichenklassen (`[\w\-\+\._]`) vor und hinter dem "@" beschreiben einen Namen aus Buchstaben, Ziffern, dem Minuszeichen, dem Underline und dem Punkt. Das deckt sich ungefähr mit Usernamen bzw. Domainangaben. Eine Gültigkeit der Adresse ist jedoch nicht feststellbar.

Der zweite Ausdruck zeigt die Zerlegung einer URL in ihre Teilkomponenten:

    ($host,$port,$file) = ($url =~ m|http://([^/:]+):{0,1}(\d*)(.*)$|);

`"http://"` steht für sich selbst, dann kommt eine Zeichenfolge, die entweder mit "/" oder mit ":" endet, also die Hostangabe. `:{0,1}` deckt sich mit einem oder keinem Doppelpunkt. Die folgende Ziffernfolge (die auch leer sein kann) wird in `$port` gespeichert und der Rest der URL in `$file`. Das Ganze geht schief, wenn der Dateiname nur aus Ziffern besteht.

#### Tipp:

Nehmen wir nochmals das Beispiel von oben und bleiben wir beim Standard-Delimiter, dem Schrägstrich. Dann sieht der reguläre Ausdruck recht gruselig aus:

    ($host,$port,$file) = ($url =~ m/http:\/\/([^\/:]+):{0,1}(\d*)(.*)$/);

Bei einem Ersetzungs-Ausdruck würde es noch unübersichtlicher. Hinzu kommen Unsicherheiten. Deshalb kann man statt der Delimiter die Terme von beim Stringvergleich und der Ersetzung auch in geschweifte Klammern setzen, um das Ganze ü,bersichtlicher zu machen.

- `=~ m{...}`: ist enthalten,
- `!~ m{...}`: ist nicht enthalten,
- `=~ s{...}{...}`: Textersetzung,
- `=~ tr{.}{.}`: Zeichenersetzung

### 2.7 Unterprogramme

Unterprogramme ermöglichen die Strkturierung des Programms. Mehrfach im Programm benötigter Code muß nur einmal aufgeschrieben werden. In Perl erfolgt die Definition mit dem "sub"-Schlüsselwort:

    sub name
      { ... }

Zum Aufruf von Funktionen wird in der Regel "&name" oder "name()" verwendet.

Unterprogramme können auch einen beliebigen Wert zurückliefern, man nennt sie dann auch "Funktionen". Damit ergibt sich folgender allgemeiner Aufbau:

    sub myfunc
      {
      ... Anweisungsteil ...

      return returnvalue;
      }

Unterprogrammen können auch Parameter mitgegeben werden, es erfolgt jedoch im Gegensatz zu C keine formale Definition der Parameter. Ab Perl 5.003 darf auch ein Prototyp für die Parameter mitgegeben werden, indem die Zeichen "$", "@" und "%" als Platzhalter für Parameter vom jeweiligen Typ stehen., z. B. `sub myfunc($$)`. Hier ist dann der Aufruf nur mit zwei Skalarparametern erlaubt.

Eigenschaften von Funktionen:

- In Perl können Sie Ihre Funktionen an beliebigen Stellen im Programm definieren.
- Perl kennt keine formale Parameterliste in der Kopf-Deklaration wie Pascal oder C/C++, sondern arbeitet mit einer variablen Anzahl schwach typisierter Parameter.
- In Perl können Sie mehrere Rückgabewerte zurückliefern. Des Weiteren kann der Kontext (wantarray: Liste oder Skalar) abgefragt werden.
- In Perl stehen alle Argumente, die an eine Funktion übergeben werden, als Parameterliste im Spezial-Array `@_` zur Verfügung. Über `@_` (Spezial-Array) bzw. `$_[0], $_[1], ...` (Spezial-Variable) usw. sind sie erreichbar.
- Veränderungen des Spezial-Arrays `@_` haben Auswirkungen bei den Parameter-Variablen im Hauptprogramm. Wenn Sie also eine Variable an eine Funktion übergeben, ergibt das Element aus dem Spezial-Array `@_` einen Alias zu dieser Variablen (implizierte Referenzen, Variablen-Parameter).
- Zur Realisierung des Übergabemechanismus call-by-value (Kopie der Werte der Aufrufparameter) werden die jeweiligen Werte des Spezial-Arrays `@_` lokalen Variablen innerhalb der Funktion zugewiesen.
- Mit Perl ist ein einfaches Prototyping möglich (siehe oben).
- Perl besitzt etliche eingebaute Funktionen, die der Perl-Interpreter kennt und die vor ihrer Verwendung nicht deklarieren werden müssen.

Für den Aufruf der Funktion `myfunc` gibt es in Perl drei Möglichkeiten (wobei die erste die häufigste ist):

    myfunc (Parameterliste);
    myfunc Parameterliste;
    &myfunc;

Die Argumentenübergabe erfolgt als Liste von Skalaren oder Referenzen innerhalb des Defaultübergabearrays "`@_`". Um in der Funktion auf die Parameter zugreifen zu können, verwendet man die üblichen Array-Funktionen.

Um Funktionen aus anderen Paketen zu importieren verwendet man folgenden Befehl:

    use PAKETNAME qw(NAME1 NAME2 ...);

wobei "NAME1" usw. für die zu importierende Funktion aus "PAKETNAME" steht. Die Funktion "`qw`" behandelt die Namen als Liste, ohne daß das Komma benötigt wird.

Vordefinierte Perl-Funktionen reichen von arithmetischen Funktionen und Funktionen für die Zeichenkettenmanipulation bis zu Datenbank-, Netzwerk- und Prozesskommunikations-Funktionen.

Einige Beispiele:

Einfachster Fall ohne Wertübergabe:

    sub zaehle
      {
      my $i;                    # lokale Variable $i
      $summe = 0;
      for ($i=1; $i <= 10; $i++ )
        {
        $summe = $summe + $i;
        }
      }

    &zaehle;
    print "Die Summe betraegt: $summe\n";

Wertübergabe ans Unterprogramm:

    sub gibaus
      {
      foreach $listeninhalt (@_)
        { print $listeninhalt, "\n"; }
      }

    @tabelle = (Tick, Trick, Track);
    gibaus(@tabelle);

Aufruf als Funktion, es werden Werte zurückgeben.

    sub gibaus
      {
      my(@liste) = @_; # Parameteruebergabe an lokales Array @liste
      foreach $listeninhalt (@liste)
        { print $listeninhalt, "\n"; }
      return $#liste;
      }

    @tabelle = (Tick, Trick, Track);
    $anzahl = gibaus(@tabelle);
    print "Tabelle hat: ", $anzahl+1, " Elemente\n";

#### Rückgabewerte

Für die Rückgabe von Werten stehen zwei Methoden zur Verfügung:

- explizit: `return $x;`
- implizit: der Wert des letzten ausgewerteten Ausdrucks wird automatisch übergeben.

Dieses Unterprogramm berechnet das Maximum von zwei gegebenen Eingabeparametern:

    sub maximum
      {
      if ($_[0] > $_[1]) { $_[0]; }
      else                  { $_[1]; }
      }

Der Aufruf sieht so aus:

    $max = maximum(37, 24);

Noch kürzer wä natürlich die folgende Variante:

    sub maximum
      {
      ($_[0] > $_[1])? $_[0]:$_[1];
      }

Das Resultat einer erfolgreichen `print`-Anweisung ist immer 1. Man kann den Rückgabewert aber auch explizit mit `return Wert` an das aufrufende Programm zurückgeben.
Noch eine Funktion mit Parameterübergabe und Rückgabewert:

    sub Rechne
      {
      # eval() betrachtet einen String als Rechenausdruck
      # und gibt das errechnete Ergebnis zurück.
      return eval($_[0]);
      }

    $x = Rechne(1 + 2 * 3 + 4 * 5);
    print $x, "\n";

Die einzelnen Parameter werden, wie oben erwähnt, mit $_[0], $_[1], ... angesprochen und müssen deshalb nicht als formale Parameter definiert werden. Auf die Parameter kann auch über die `shift`-Funktion zugegriffen werden:

    sub max
      {
      my $max = shift @_;		# erster Parameter
      foreach my $tmp (@_)		# Schleife ueber die restlichen Parameter
        {
        $max = $tmp if ($max < $tmp);
        }
      return $max;
      }

Diese Parameter haben nichts mit der Spezialvariablen "$_" zu tun und man kann beides verwenden, ohne Namenskollisionen befürchten zu müssen.

Variable, die innerhalb eines Unterprogramms implizit definiert werden, behalten nach Beenden des Unterprogramms ihre Gültigkeit und sind damit globale Variablen. Die Verwendung des gleichen Namens im Haupt- und Unterprogramm kann zu Fehlern führen. Die Lebensdauer einer Variablen kann aber auf den Block des Unterprogramms beschränkt werden, indem sie explizit mit "my" oder "local" als lokal definiert wird. Es wird empfohlen die ältere Methode mit "local" nicht mehr zu verwenden.

Beispiel 1 (globale Variable):

    my $x = 10;
    &Rechne;
    print "HP: ", $x, "\n";

    sub Rechne
      {
      $x = 5;
      print "UP: ", $x, "\n";
      }

Beispiel 2 (lokale Variable):

    my $x = 10;
    &Rechne;
    print "HP: ", $x, "\n";

    sub Rechne
      {
      my $x = 5;
      print "UP: ", $x, "\n";
      }

Die folgende Subroutine testet, ob ein String Substring eines anderen ist, ohne die Leerzeichen zu berücksichtigen:

    sub inside
      {
      my ($a, $b);                    # Erzeuge lokale Variable
      ($a, $b) = ($_[0], $_[1]);      # Param. zuordnen
      $a =~ s/ //g;                   # Leerzeichen aus lokalen
      $b =~ s/ //g;                   # Variablen loeschen
      ($a =~ /$b/ || $b =~ /$a/);     # Ist $b in $a  oder $a in $b?
      }

    inside("lemon", "dole money");    # true

Falls mehr als eine Variable mit `my` deklariert werden soll, müssen sie wie oben in Klammern angegeben werden (`my` ist eine Funktion!).

`local` war in Perl 4 die einzige Möglichkeit lokale Variabeln zu deklarieren. Aus Kompatibilitätsgründen und für Spezialfälle ist `local` noch verfügbar. `my` ist die neuere (ab Perl 5) und effizientere Art lokale Variablen zu deklarieren. Der Unterschied zwischen `my` und `local` ist subtil:

- `local` erzeugt einen temporären Wert für eine globale Variable (dynamic scoping),
- `my` deklariert dagegen eine echte lokale Variable (lexical scoping), welche nur im umgebenden Block bekannt ist.

Die Syntax ist bei beiden gleich. Eine Einschränkung gibt es nur bei den Variablennamen: Bei `my` sind nur alphanumerische Zeichen erlaubt! Um Spezialvariablen, wie zB. "$\" zu lokalisieren, muß nach wie vor `local` verwendet werden.

Will man mehr als ein Array oder Hash der Routine übergeben oder als Rückgabewert erhalten, dann geht das nur mit Hilfe von Referenzen.

Rückgabewerte mit verschiedenem Kontext wie zum Beispiel die eingebaute Funktion `localtime`, die je nach Kontext verschiedene Werte zurückliefert, kann man auch in eigenen Funktionen realisieren. Dabei hilft die Funktion `wantarray`:

    sub foobar
      {
      ....
      if (wantarray)
        {
        return @array;
        }
      else
        {
        return $scalar;
        }
      }

#### Vordefinierte Perl-Funktionen für Zeichenketten

sort(@Liste) | Sortieren einer Liste. Funktionsaufruf: "sort(@Liste);" oder "sort @liste;".
split(Pattern, Ausdruck) | zerlegt einen Zeichenkette-Ausdruck an einem bestimmten Zeichen und weist das Ergebnis einem Arry zu. Der Funktionsaufruf "@liste = split(/ /, $string);" zerlegt eine Zeile in Worte.
join(Ausdruck, Liste) | vereint die Elemente einer Liste zu einem String, wobei die Elemente durch das Ergebnis des Ausdrucks verbunden werden z. B. $s = join('-',@liste);.
grep(Ausdruck Liste) | Sucht alle Elemente einer Liste, die auf einen regulären Ausdruck passen, z. B. liefert "@result = grep(/^M/, @liste);" alle Elemente von @liste, die mit "M" beginnen. Beispiel:
    print "Geben Sie eine Folge von Begriffen ein, getrennt durch Komma:\n";
    $Eingabe = <STDIN>;
    chomp $Eingabe;
    @Liste   = split(/,/, $Eingabe);
    @SortierteListe = sort(@Liste);
    for(@SortierteListe)
      { print $_, "\n"; }

Teilstring = substr(String, Pos) | liefert den Rest des Strings ab der Position "Pos".
Teilstring = substr(String, Pos, Anz) | liefert "Anz" Zeichen aus String ab der Position "Pos".
Pos = index(String1, String2) | liefert die Position an der "String2" erstmals im "String1" vorkommt. "String2" kann auch nur ein Zeichen sein. Ist "Pos" = 0 wurde String2 nicht gefunden.
Pos = rindex(String1, String2) | liefert analog das letzte Vorkommen von "String2" in "String1".
length (String) | liefert die Länge des Strings.

Das folgende Beispiel zeigt die Anwendung der verschiedenen Funktionen:

    $Url = "http://www.netzmafia.de/skripten/sicherheit/index.html";
    $Stop = index($Url,":");
    $Protokoll = substr($Url,0,$Stop);
    $Start = index($Url,"//") + 2;
    $Domain = substr($Url,$Start);
    $Stop = index($Domain,"/");
    $Domain = substr($Domain,0,$Stop);
    $Start = rindex($Url,"/") + 1;
    $Dateiname = substr($Url,$Start);
    $Url_Laenge = length($Url);
    print "Protokoll.....: ", $Protokoll, "\n";
    print "Domain-Adresse: ", $Domain, "\n";
    print "Dateipfad.....: ", $Dateiname, "\n";
    print "Laenge........: ", $Url_Laenge, "\n";

Perl besitzt weiterhin Funktionen für die wichtigsten höheren mathematischen Berechnungen, darunter:

- abs: Absolutwert
- sqrt: Quadratwurzel,
- int: ganzzahliger Anteil
- exp: Potenz zur Basis e
- log: nat. Logarithmus
- sin: Sinus
- cos: Cosinus

Befehle zum Ausführen von Kommandos auf Systemebene:

- system("Kommando"): Angegebenes Kommando ausführen, Rückgabewert ist der Returncode des extermen Programms. Das Kommando wird in einer Subshell ausgeführt, wenn nur eine einzige Kommandozeile übergeben wird (z.B.: `system("ls -la");`), sonst wird das Kommando direkt geforkt (z.B.: `system("ls", "-la");`).
- exec("Kommando"): Aktuelles Perlprogramm mit dem angegebenen Kommado überlagert, keine Rückkehr zum Perl-Programm.
- $var = `Kommando`: Angegebenes Kommando ausführen, in $var gelangt die Ausgabe des extermen Programms.

Beispiel: Wochentagsberechnung

    sub dayofweek
      {
      # Liefert den Wochentag für ein Datum
      # Eingabeparameter: Tag, Monat and Jahr.
      #             z.B.: &dayofweek(18, 9, 2004);
      #  Rueckgabewert: 0=Sonntag, 1=Montag, ...
      my ($day, $month, $year) = @_;
      my ($a, $y, $m, $dow);
      my @daysinmonth = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
      # Schaltjahr?
      $daysinmonth[2]++ if ((($year%4 eq 0) && ($year%100 ne 0)) || ($year%400 eq 0));

      if (($month < 1 || $month > 12) || ($day < 1 || $day > $daysinmonth[$month]))
        {
        return(-1);
        }
      else
        {
        $a = int((14 - $month)/12);
        $y = $year - $a;
        $m = $month + (12 * $a) - 2;
        $dow = ($day + $y + int($y/4) - int($y/100) + int($y/400) + int((31 * $m)/12)) % 7;
        return($dow);
        }
      }

#### Blöcke

In Perl gibt es Unterprogramme, die automatisch zu bestimmten Zeitpunkten der Übersetzung oder Ausführung des Skripts aufgerufen werden. Sie heißen BEGIN, CHECK, INIT, und END. Das Schlüsselwort "sub" kann bei diesen Unterprogrammen auch weggelassen werden (was auch meist geschieht). Sie werden daher auch oft "Blocks" genannt (BEGIN-Block, usw.), weil sie mehr als Neinrmale Unterprogramme sind.

Im Gegensatz zu Subs können Blocks mehrmals definiert werden. Perl faßt den Code zusammen, was möglich ist, weil sie nicht im Programm aufgerufen werden, sondern vom Interpreter.

Die vier Blocks werden in folgender Reihenfolge aufgerufen:

1. BEGIN
   wird sofort nach dem Parsen des Quellcodes aufgerufen, noch bevor der Rest des Programms compiliert wird.
2. CHECK
   wird sofort aufgerufen nachdem das Progamm komplett compiliert wurde - noch vor allem anderen. "check" steht also nicht für "Überprüfung, sondern für "checkpoint".
3. INIT
   wird nach CHECK und Initialisierungen in hinzugelandenen Modulen ausgeführt, aber vor dem ersten ausführbaren Befehl des Programms.
4. END
   wird ausgeführt, nachdem das normale Programm endete (auch bei einen Abbruch durch einen Fehler).

Es werden also zuerst alle BEGIN-Blöcke, dann alle CHECK-Blöcke und danach alle INIT-Blöcke abgearbeitet, bevor das reguläre Programm beginnt. Nach Ablauf des Programms werden alle END-Blöcke abgespult. Mehrere BEGIN- und INIT-Blöcke laufen in der Reihenfolge ihres Auftretens ab. Mehrere CHECK- und END-Blöcke in der umgekehrten Reihenfolge (also der letzte zuerst).

Dazu ein Beispiel:

    #!/usr/bin/perl -l
    print       "Hauptprogramm laeuft";
    die         "Hauptprogramm stirbt\n";
    die         "XXX: nie erreicht!\n";
    END         { print "1st END: done running"   }
    CHECK       { print "1st CHECK: done compiling" }
    INIT        { print "1st INIT: started running"  }
    END         { print "2nd END: done running"   }
    BEGIN       { print "1st BEGIN: still compiling" }
    INIT        { print "2nd INIT: started running"  }
    BEGIN       { print "2nd BEGIN: still compiling" }
    CHECK       { print "2nd CHECK: done compiling" }
    END         { print "3rd END: done running"   }

Wenn das Programm gestartet wird, erzeugt es folgende Ausgabe:

    1st BEGIN: still compiling
    2nd BEGIN: still compiling
    2nd CHECK: done compiling
    1st CHECK: done compiling
    1st INIT: started running
    2nd INIT: started running
    Hauptprogramm laeuft
    Hauptprogramm stirbt
    3rd END: done running
    2nd END: done running
    1st END: done running

Ein BEGIN-Block kann also Definitionen, Unterprogramm-Deklarationen usw. ausführen, die auf die anschließende Compilierung des Programms Einfluß haben können (z. B. compiler-spezifische Werte überschreiben).

END-Blöcke werden dagegen so spät wie möglich ausgeführt. Sie werden nur dann nicht ausgeführt, wenn sich das Programm per "exec" durch ein anderes Programm ersetzt hat, oder wenn der Prozeß durch ein Signal gekillt wird, das nicht abgefangen wurde. Innerhalb eines END-Blocks enthält die Variable $? den Exit-Code des Programms, kann aber geändert werden.

Die folgende Tabelle faßt alle Möglichkeiten zusammen (C = Compilationsphase, R = Ausführungsphase (Run)).

Block oder Ausdruck | Phase der Compilation | Fängt Compilations- fehler ab | Ablauf in Phase | Fängt Laufzeit- fehler ab
--- | --- | --- | --- | ---
use … | C | Nein | C | Nein
Nein … | C | Nein | C | Nein
BEGIN {…} | C | Nein | C | Nein
CHECK {…} | C | Nein | C | Nein
INIT {…} | C | Nein | R | Nein
END {…} | C | Nein | R | Nein
eval {…} | C | Nein | R | Ja
eval "…" | R | Ja | R | Ja
foo(…) | C | Nein | R | Nein
sub foo {…} | C | Nein | R | Nein
eval "sub {…}" | R | Ja | R | Nein
s/pat/…/e | C | Nein | R | Nein
s/pat/"…"/ee | R | Ja | R | Ja

### 2.8 Dateien

Im folgenden wird davon ausgegangen, daß das Perl-Script die passende Zugriffsrechte für Dateien und Verzeichnisse besitzt. Bevor man von einer Datei lesen oder auf eine Datei schreiben kann, muß man diese Datei öffnen, dabei wird ihr ein File-Handle zugeordnet, das für die weiteren Zugriffe verwendet wird. Beim Öffnen wird bereits angegeben, ob die Datei gelesen oder beschrieben werden soll. Zum Lesen muß die Datei bereits existieren. Soll eine Datei zum Schreiben geöffnet werden, die bereits existiert, so wird der alte Dateiinhalt überschrieben. Existiert die zu beschreibende Datei noch nicht, wird eine neue Datei mit dem angegebenen Namen angelegt.

Zum Öffnen einer Datei dient der Befehl

    open(FileHandle,'Dateibezeichnung und Öffnungsmodus')

Als File-Handles verwendet man üblicherweise Namen, die nur aus Großbuchstaben gebildet werden, um sie von anderen Variablen zu unterscheiden. Es steht auch kein Dollarzeichen vor dem Handle. "STDIN" und "STDOUT" bezeichen die Filehandles für die Standardeingabe und -ausgabe.

    open(SESAM, "filename");               # lesen aus existierender Datei
    open(SESAM, "<filename");              # dito (explizit)
    open(SESAM, ">filename");              # Datei erzeugen und schreiben
    open(SESAM, ">>filename");             # an Datei anhängen
    open(SESAM, "|output-pipe-command");   # Ausgabepipe erzeugen (s. unten)
    open(SESAM, "input-pipe-command|");    # Eingabepipe erzeugen (s. unten)

Zum Schließen einer Datei verwendet man das Kommando

    close(FileHandle)

#### Lesender Zugriff

Zum Lesen aus dem Filehandle wird der "<>"-Operator benutzt, damit liefert "<FILEHANDLE>" immer die nächste Zeile aus der Datei. Am Dateiende liefert dieser Operator einen Nullwert als Ergebnis. Das Einlesen des Dateiinhaltes kann zeilenweise erfolgen. Mit:

    $Zeile = <SESAM>;

wird jeweils die nächste noch nicht gelesene Zeile eingelesen und der links stehenden Variablen als Wert zugewiesen. Man kann auch alle Zeilen auf einen Schlag lesen und sie als Werte einer Liste abspeichern:

    @Zeilen = <SESAM>;

Man kann sogar noch weiter gehen und die gesamte Datei in einer einzigen Zeile abspeichern:

    @Zeilen = <SESAM>;
    $line = join('', @Zeilen);

Das ist dann ganz praktisch, wenn man in der gesamten Datei Ersetzungen oder Umformungen vornehmen will. Mit `print $line;` wird dann der gesamte Dateiinhalt weggeschrieben. Die Newline-Zeichen bleiben dabei erhalten!

Das Öffnen einer Datei ist immer eine fehlerträchtige Situation, man sollte deshalb im Fehlerfall eine Fehlermeldung ausgeben, z. B.:

    open(SESAM,'daten.dat') || die "Fehler beim Ö der Eingabedatei: $!";
         ...

Der Befehl "die" ("stirb") beendet das Programm mit einer Fehlerausgabe, er wird aufgerufen, falls das Öffen der Datei fehlschlägt.

Perl liefert beim Einlesen eines Satzes auch das Zeichen, mit dem das jeweilige Ende des Datensatzes angegeben wird. Üblicherweise ist dies Newline(\n). Wenn man dieses Zeichen nicht im Eingabestring haben möchte, muß man es explizit entfernen. Der Befehl dazu ist:

    chop($Satz);

er entfernt das jeweils letzte Zeichen der Eingabe. Manchmal ist der Gebrauch von chop ungünstig, da immer das letzte Zeichen des Strings entfernt wird, auch wenn es kein Newline ist. Da hilft

    chomp($Satz);

Mit chomp wird ein Newline am Stringende entfernt. Alle anderen Zeichen werden verschont.

#### Schreibender Zugriff

Zum Schreiben öffnet man eine Datei mit:

    open(File-Handle,">Dateiname") || die "Fehler beim Ö der Ausgabedatei: $!";

wenn ein möglicherweise vorhandener Dateiinhalt überschrieben werden soll. Sollen Daten an eine vorhandene Datei angehängt werden, öffnet man die Datei mit:

    open(File-Handle,">>Dateiname") || die "Fehler beim Ö der Ausgabedatei: $!";

Die Ausgabe in die Datei erfolgt mit der print-Anweisung:

    print SESAM  Ausgabedaten

Geschrieben wird damit in die aktuelle Ausgabezeile, Zeilenwechsel müssen in Perl explizit angegeben werden. Für Unix-Dateien ist dies das Zeichen 'newline', mit der Ersatzdarstellung "\n", bei DOS/Windows-Dateien gibt man "\r\n" aus. Beachten Sie auch, daß im Gegensatz zu anderen Programmiersprachen kein Komma zwischen Filehandle und Ausgabedaten steht, sondern ein Leerraum.

    open(LESEN,"< alt.dat") || die "Fehler beim Ö der Eingabedatei: $!";
    open(SCHREIBEN,"> neu.dat") || die "Fehler beim Ö der Ausgabedatei: $!";

    while(defined($i = <LESEN>))
      { print SCHREIBEN $i }

    close(LESEN);
    close(SCHREIBEN);

Alternativ gibt es noch eine Open-Variante mit drei Parametern. Hierbei bildet die Angabe, ob gelesen oder geschreiben wird, einen eigenen Parameter:

    open(DAT, "<", $Dateiname) || die " .... ";
    open(DAT, ">", $Dateiname) || die " .... ";

#### Ein- und Ausgabe mit Pipes

Man kann als Eingabestrom für ein Skript auch die Ausgabe eines anderen Prozesses verwenden, die Standardausgabe des anderen Prozesses wird dann in einen Eingabestrom des Perl-Skriptes gepiped.

    open(SESAM, "ls -l |");
    while ($Zeile = <SESAM>)
      {
      print $Zeile;
      }

Die Ausgabe des Unix-Kommandos 'ls -l' wird über den File-Handle SESAM zeilenweise eingelesen und auf die Standardausgabe ausgegeben. Das Kommando "ls" wirkt hier auf das Verzeichnis, in dem das Perl-Skript ausgeführt wird.

Ebenso kann man auch einen Ausgabestrom in einen anderen Prozeß pipen. Im folgenden Beispiel wird ein Ausgabestrom an ein Drucke-Kommando weitergereicht:

    open(SESAM, "| lpr");
    while (<>)
      {
      print SESAM $_;
      }

Beispiel: Mail versenden

    open(MAIL, "|/bin/mail plate\@netzmafia.de");
    print MAIL "Dies ist ein Test\n";
    close(MAIL);

Man beachte, daß hier der Klammeraffe maskiert werden muß, da er für sich selbst steht und keine Listennamen kennzeichnet.

Beispiel: Eine Datei kopieren

    open(INFILE, "eingabe");
    open(OUTFILE,"ausgabe");
    while (<INFILE>)
      {
      print OUTFILE $_;
      }
    close(OUTFILE);
    close(INFILE);

Beispiel: Vergleich User-ID und Passwort (z. B. von einem Formular) mit den Benutzerdaten in der Datei /etc/passwd unter UNIX. Diesmal als Unterprogramm definiert.

    sub validatelogin # ($user-id, $password)
      {
      my ($user,$pass) = @_;
      my $retval = 0;
      if (open(PASSWD, "/etc/passwd"))
        {
        while (<PASSWD>)
          {
          chomp;
          my ($login, $passwd, $rest) = split(/:/);
          if ($login equ $user && crypt($pass,$passwd) eq $passwd)
            {
            $retval = 1;
            last;
            }
          }
        close(PASSWD);
        }
      return $retval;
      }

Bei fast allen UNIX-Systemen wird heute jedoch eine Shadow-Passwortdatei verwendet (/etc/shadow), auf die nur der User "root" Zugriff hat, was das Beispiel etwas "akademisch" wirken läßt. Man kann es aber verwenden, wenn man für den Zugriff auf bestimmte Funktionen eine separate Passwortdatei führt (wie bei geschützten Webseiten), die ausgewertet wird.

Die Filehandles `STDIN, STDOUT` und `STDERR` sind vordefiniert. Defaultmässig schreibt `print` auf `STDOUT` und `<>` liest von `STDIN` ( `<>` ist äquivalent zu `<STDIN>`).

#### Sperren des Dateizugriffes für andere Benutzer

Bei CGI-Skripten kann es sein, daß mehrere Webserver-Prozesse gleichzeitig laufen und daher auch mehrere Clients gleichzeitig auf eine Datei zugreifen. Solange beide nur lesen wollen, ist das unproblematisch, es können aber Konflikte entstehen, wenn mehrere Clients schreiben wollen. Mit dem "flock"-Befehl lassen sich die Zugriffe anderer Benutzer auf eine Datei sperren. Die Prozesse dieser Benutzer müssen solange auf den Zugriff warten, bis Ihr Prozess den Zugriff auf die Datei wieder zuläßt. Der Befehl "flock" wird mit dem File-Handle der Datei und einer Zahl zur Spezifizierung der Sperre aufgerufen.

1 | `LOCK_SH` | shared read lock. Offener Zugriffsschutz, wird meist beim lesenden Zugriff verwendet. Andere dürfen während der Sperre die Datei lesen, schreiben darf jedoch nur der Benutzer, der die Sperre veranlaßt hat.
2 | `LOCK_EX` | exclusive write lock. Exklusiver Zugriffsschutz, erlaubt anderen während der Sperre keinerlei Zugriff auf die Datei, auch keinen Lesezugriff.
4 | `LOCK_NB` | non blocking lock. Setzt lediglich einen Vermerk auf die Datei ohne andere am Zugriff zu hindern (Nicht blockierende Sperre).
8 | `LOCK_UN` | unlock. Hebt eine Sperre wieder auf.

#### Weitere Dateibefehle

- Datei umbenennen: `rename($filename, "$filename.neu");`
- Datei löschen: `unlink ("dateiname");`
  `unlink (<*.html>);` wirkt wie `rm *.html`
- Dateiattribute testen: `if (Attribut $filename) { ... };`
  - -r lesbar
  - -w schreibbar
  - -x ausführbar
  - -o gehört dem Benutzer
  - -e existiert
  - -z leer
  - -s nichtleer (liefert die Dateigröße in Bytes)
  - -f ist eine normale Datei
  - -d ist ein Directory
  - -l ist ein symbolisches Link
  - -S ist ein Socket
  - -p ist ein FIFO
  - -b ist ein Blockdevice
  - -c ist ein Zeichengerät
  - -t ist ein Ausgabegerät
  - -T enthält Text
  - -B enthält Binärdaten
  - -M Zeit seit der letzten Änderung in Tagen
  - -A Zeit seit dem letzten Zugriff
- Anlegen eines Verzeichnisses: `mkdir ("VerzName", 0777);`
- Löschen eines Verzeichnisses: `rmdir ("verzname");`
- Zugriffsrechte ändern: `chmod(0666,"dateiname");`

#### Binäre Daten und Dateien

In Perl werden Binärdateien ähnlich wie in C behandelt: Für das Lesen gibt es die Funktion `read`, fürs Schreiben `print`. Zum Positionieren dient `seek` und um die aktuelle Position im File zu erhalten, verwendet man `tell`.
Diese Funktionen sind auch für Textdateien anwendbar, werden jedoch meistens in Dateien mit fester Recordlänge verwenden, wo ein wahlfreier Zugriff auf einzelne Sätze durch einfache Berechnungen möglich ist.

Einfaches Beispiel: Kopie einer Datei:

    open FROM, "InFile";
    open TO, ">OutFile";
    while (read FROM, $buf, 16384)
      {
      print TO $buf;
      }
    close FROM;
    close TO;

Bei Betriebssystemen wie DOS oder Windows müssen die Dateien noch auf den Binärmodus umgestellt werden, indem nach den `open`-Befehlen

    binmode(FROM);
    binmode(TO);

eingefügt wird. Bei UNIX oder Mac-OS ist das nicht nötig.

Nehmen wir an, wir hätten eine Datei, die von einem C-Program erzeugt wurde und zwar mit fester Satzlänge. Wir wollen diese Sätze auslesen und auf den Inhalt der einzelnen Felder zugreifen. Dazu verwenden wir die Funktionen `read` und `unpack`. `unpack` benötigt als Parameter ein Template, welches die Struktur des Satzes beschreibt und einen String, welcher den Satz beinhaltet. Das Template ist eine Folge von Buchstaben, welche die Reihenfolge und Art der einzelnen Felder des Records beschreibt:

- `A,a`: ASCII String
- `b,B`: Bit String
- `h,H`: Hex String

- `c,C`: Signed/Unsigned Char
- `s,S`: Signed/Unsigned Short
- `i,I`: Signed/Unsigned Integer
- `l,L`: Signed/Unsigned Long

- `f`: Float
- `d`: Double

- `p`: Pointer to a null-terminated string.

Weitere Templates sind in den Manualpages zur Funktion pack zu finden.

Ein Teil des C-Codes für obiges Beispiel sieht etwa folgendermaßen aus:

    struct
      {
      char st[10];
      int in;
      double d;
      } drec;
      ...

      fwrite(&drec, sizeof(drec) 1 , fp);
      ...

Perl-Code zum Lesen der Datei:

    $template = "a10 i d";
    $len = length pack($template,'',0,0);
    open FP, "filename";
      ...
      read(FP,$rec,$len);
      ($str,$in,$d) = unpack($template,$rec);
      ...

Mit `pack` kann ein Record wie bei C erzeugt werden. Wir brauchen diese Funktion im obigen Beispiel nur, um die Länge des Records zu bestimmen.

#### Ausgabe-Formatierung

Neben "print" kennt Perl auch die Ausgabefunktion "printf", die wie bei der Programmiersprache C definiert ist (siehe auch C-Einführung). Erster Parameter ist ein Formatstring, in dem Platzhalter für die auszugebenden Variablen eigestreut sind. Ein Platzhalter (besser: eine Formatanweisung) beginnt immer mit einem %-Zeichen. Danach folgt optional die Feldbreite und ein Buchstabe. Die Ausgabe kann rechts- oder linksbündig sein. Bei Gleitpunktzahlen kann die Anzahl der Ziffern vor und hinter dem Dezimalpunkt festgelegt werden (z. B. %10.2f). Strings lassen sich mit %s entweder linksbündig (Minus-Zeichen mit Feldbreite) oder rechtsbündig (Plus-Zeichen mit Feldbreite) einpassen. Die wichtigsten Formatanweisungen sind in der folgenden Tabelle zusammengefasst:

%c | Zeichen (Char) im ASCII-Code
%s | String
%d | Zahl in Dezimaldarstellung
%U | Unsigned Integer, Dezimal
%0 | Unsigned Integer, Oktal
%x | Unsigned Integer, Umwandlung in Hexadezimal
%X | Wie %x, nur 'A'-'F' statt 'a'-'f'
%e | Exp.Darstellung
%E | Wie %e
%f | Gleitkommazahl, Festpunktdarstellung
%g | Gleitkommazahl in %e oder %f

Beispiel: Ausgabe in den verschieden Formaten. Die Feldbreite der Ausgabe ist zwölf Zeichen lang. Zwei Stellen der Gleitpunkzahl sollen als Nachkommastelle verwendet werden:

    my $string = "Perlisch ist nicht schwer!";
    my $int  42;
    my $pi = 3.14159;

    printf ("String linksbuendig      %-12s\n",$string);
    printf ("String rechtsbuendig     % 12s\n",$string);
    printf ("Integer linksbuendig     %-12d\n",$int);
    printf ("Integer rechtsbuendig    % 12d\n",$int);
    printf ("Gleitpunkt               %f\n",$pi);
    printf ("Gleitpunkt als Exp.      %E\n",$pi);
    printf ("Gleitpunkt linksbuendig  %-10.2f\n",$pi);
    printf ("Gleitpunkt rechtsbuendig %10.2f\n",$pi);

Perl kann auch einfache Reports und Tabellen auszugeben. Man deklariert das Layout der Ausgabe mit `format` und gibt die einzelnen Records mit `write` aus. Die Formatdefinition kann irgendwo im Programm erfolgen und hat die folgende Syntax:

    format FILEHANDLE =
    fieldline_1
    valueline_1
    ...

    fieldline_n
    valueline_n
    .

Definiert ein Ausgabeformat für die Datei `FILEHANDLE`. `fieldline_i` definiert das Aussehen einer Zeile und `valueline_i` listet alle Werte und Variablen, die ausgegeben werden sollen. Zum Beispiel wird durch das Format die Ausgabe der Werte der Variablen $a, $b, $c und $d auf STDOUT definiert.

    format STDOUT =
    @#### @<<<<<  @|||||  @>>>>>
    $a, $b, $c, $d
    .

Dabei wird durch die "#"-Reihe $a als Zahl in einem Feld der Länge 5 formatiert, durch "<" das erste $b wird als Zeichenkette linksbündig in einem Feld der Länge 6 formatiert, $c wird durch die senkrechten Striche als Zeichenkette zentriert in einem Feld der Länge 6 formatiert und schließlich wird $d als Zeichenkette rechtsbündig (">") in einem Feld der Länge 6 formatiert. Mit "write FILEHANDLE;" wird ein Datensatz entsprechend dem Format mit den aktuellen Werten der Variablen geschrieben.
`format FILEHANDLE_TOP = ...` definiert entsprechend ein Format für den Kopf einer Ausgabeseite.

Beispiel: Die Binärdatei von oben soll schön formatiert ausgegeben werden:

    format STDOUT_TOP =
       STRING       INTEGER        DOUBLE
       ----------------------------------
       .

    format STDOUT =
       # String linksbuendig, Integer zentriert, Double rechtsbuendig
       @<<<<        @||||||        @>>>>>
       $str,$in,$d
       .

    ...

    while (read(FP,$rec,$len))
      {
      ($str,$in,$d) = unpack($template,$rec);
      write;
      }

    ...

Bei mehrzeilignen Texten kann man die Textfelder mit dem gleichen Variablennamen wiederholen, zum Beispiel:

    format STDOUT =
       # String linksbuendig, drei Zeilen
       @<<<<<<<<<<<<
       $str
       @<<<<<<<<<<<<
       $str
       @<<<<<<<<<<<<
       $str
       .

Ist der String kurz, werden gegebenenfalls Leerzeilen ausgegeben. Bei mehrzeiligen Textfeldern, bei denen man nicht weiss, wieviele Zeilen entstehen, beginnt man die Wiederholungszeile mit '~~'. Es werden dann dem Text entsprechend viele Zeilen generiert. Beispiel:

    format STDOUT =
       # String linksbuendig, drei Zeilen
       @<<<<<<<<<<<<
       $str
    ~~ @<<<<<<<<<<<<
       $str
       .

#### Verzeichnisse bearbeiten

Unter Perl lassen sich auch Verzeichnisse bearbeiten (ein komplexes Beispiel finden Sie in Form der lokalen Suchmaschine). Dazu wird ein Verzeichnis mit `opendir` geöffnet. Mit `readdir` lassen sich die Einträge lesen und mit `closedir` das Verzeichnis wieder schließen:

    opendir(DIR, "*") || die "Oops!"
    while ($eintrag = readdir(DIR))
      {
      print "$eintrag\n";
      }
    closedir(DIR);

Das folgende Script traversiert einen Dateibaum. Es wird ein Startverzeichnis angegeben und alle Dateien in diesem Verzeichnis aufgelistet. Danach werden rekursiv alle im Startverzeichnis enthaltene Unterverzeichnisse bearbeitet. Lediglich Dateien, die mit einem "." beginnen werden ausgelassen. Dies sind zum einen die Verzeichnisse "." und "..", deren Bearbeitung zu endlosen Schleifen führen würde, und alle "versteckten" Dateien von UNIX. Im folgenden Beispiel wird nur der Dateiname ausgegeben - es lassen sich natürlich die Dateien auch beliebig bearbeiten.

    #!/usr/bin/perl
    # perl script to traverse a file-tree
    # File beginning with a '.' are omitted

    my $path = $ARGV[0];

    &scan_files($path);

    print "\n";
    exit(0);

    sub scan_files
      {
      my (@scandirs,$scandir,@files,$file,$list);
      $scandir = @_[0];
      opendir(DIR,$scandir) || warn "can't open the directory $scandir: $!\n";
      @scandirs = grep {!(/^\./) && -d "$scandir/$_"} readdir(DIR);
      rewinddir(DIR);
      @files=grep {!(/^\./) && -f "$scandir/$_"} readdir(DIR);
      closedir (DIR);
      for $list(0..$#scandirs)
        {
        &scan_files($scandir."/".$scandirs[$list]);
        }
      if ($#files > 0)
        {
        print "<b>$scandir</b> contains the following files:\n";
        foreach $file(@files)
          {
          print "$file\n";
          }
        }
      return 1;
      }

Wenn man in der Zeile `@files=grep {!(/^\./) && -f "$scandir/$_"} readdir(DIR);` den Ausdruck erweitet, kann auch die Menge der Dateien eingeschränkt werden, z. B. würde das Anhängen von `&& (/\.pm$|\.pl$/)` nur noch die Perl-Quellen und -Module berücksichtigen.

Eine weitere Möglichkeit, auf Verzeichnisse zuzugreifen ist das sogenannte "globbing". Man wendet die Metazeichenersetzung der Shell auf eine Verzeichnisangabe an und erhält eine Array mit den dazu passenden Dateinamen. Das folgende Unterprogramm erzeugt eine Datei "index.html" mit Links auf alle HTML-Dateien eines Verzeichnisses ($HTMLPATH enthält einen Dateipfad):

    sub make_index
      {
      open (IND,">$INDEX") || die "Kann $INDEX nicht anlegen!\n";
      print IND "<HTML>\n";
      print IND "<head><title>Gesamtindex</title>\n";
      print IND "</head>\n";
      print IND "<body bgcolor=\"#ffffff\" text=\"#000000\" ";
      print IND "link=\"#0000ff\" vlink=\"#cc00cc\">\n";
      print IND "<H2 ALIGN=CENTER>Überschrift</H2>\n";
      $searchpath = $HTMLPATH . "*.html";
      @files = glob($searchpath);
      foreach $eintrag1 (@files)
        {
        $eintrag1 =~ s!^.*/!!;
        if ($eintrag1 ne 'index.html')
          {
          $eintrag2 = $eintrag1;
          $eintrag2 =~ s/.html//;
          print IND "<A HREF=\"$eintrag1\">$eintrag2</A><BR>\n";
          }
        }
      print IND "</body>\n";
      print IND "</html>\n";
      close(IND);
      }

#### Dateifunktionen

Zum Arbeiten mit Dateien gibt es folgende Funktionen:

- Änderung des Namens: `rename()`

      rename("alter_name","neuer_name");

- Löschen einer Datei: `unlink()`

      unlink("dateiname");

- Erzeugen eines Verzeichnisses: `mkdir()`

      $permissions = 0777;
      mkdir("dirname",$permissions);

- Löschen eines (leeren) Verzeichnisses: `rmdir()`

      rmdir("dirname");

- Hard Link erzeugen: `link()`

      link("filename","linkname");

- Soft Link erzeugen: `symlink()`

      symlink("filename","linkname");

- Ändern der Zugriffsrechte: `chmod()`

      $permissions = 0755;
      chmod($permissions,"prog_1","prog_2","prog_3");

- Ändern des Besitzers: `chown()`

      $uid = 100;                      # Benutzer-ID
      $gid = 10;                       # Gruppen-ID
      chown($uid,$gid,"datei");

- Ändern des Zeitstempels: `utime()`

      $access = 812_000_000;            # letzter Zugriff
      $modif =  822_000_000;            # letzte Aenderung
      utime($access,$modif,"datei");

  Die Zeiten sind hierbei in Sekunden seit dem 1.1.1970 00:00 Uhr GMT anzugeben. Die aktuelle Systemzeit kann über den Operator `time` abgefragt werden.

#### Besonderheiten der Konsole

Bei Ein- und Ausgabe über Bildschirm und Tastatur gibt es ein Problem. In der Regel erfolgen Ein- und Ausgabe gepuffert, d.h. erst beim Newlinezeichen wird wirklich ein- oder ausgegeben. Damit sind das Auslösen von Aktionen mit einem einzigen Tastendruck ebenso wenig möglich wie z:b: die Ausgabe eines Fortschrittsbalkens. Bei der Ausgabe läßt sich das Problem recht einfach lösen. Es genügt die Zeile

    $| = 1;

um die Pufferung abzuschalten.

Bei der Eingabe ist es kniffliger. Man muß das Terminal in den ungepufferten Modus schalten, was mit Hilfe des `stty`-Kommandos auf Systemebene erfolgen kann:

    # Eine einzige Taste einlesen
    sub inkey
      {
      my ($key, $dummy);
      $dummy = `stty raw < /dev/tty > /dev/tty 2>&1`;
      $key = getc(STDIN);
      $dummy = `stty cooked < /dev/tty > /dev/tty 2>&1`;
      return(substr($key,0,1));
      }

Anstelle von `stty cooked ...` kann man auch `stty sane ...` verwenden und statt der Backticks auch die Funktion `system()`. Oder man holt sich ein Perl-Modul, in dem eine passende Funktion realisiert ist.

### 2.9 Referenzen

Bei Referenzen handelt es sich um einen relativ neuen Datentyp; es gibt ihn erst seit Perl 5.003. Die Notwendigkeit ergab sich aus der Situation, daß es nicht möglich war in Perl geschachtelte Arrays bzw. Hashes zu erzeugen. Wie bereits in Kapitel Arrays beschrieben, werden die Elemente eines Array2 innerhalb eines anderen Array1 so behandelt, als wären es Elemente von Array1. Somit geht die Information verloren, welche Elemente zu Array2 gehören. Benötigt werden geschachtelte Arrays z. B. zur Erzeugung von Matrizen. Auch Perls Variante der objektorientierten Programmierung basiert intensiv auf dem Konzept der Referenzen. Mit Referenzen kann man also mehrdimensionale Arrays in Perl realisieren - oder Konstrukte, die noch weit komplexer sind. Referenzen sind immer Skalare, benötigen also den Dereferenzierer $ zu Beginn ihres Namens.

Normalerweise ist es nicht möglich, mehrere Arrays einem Array zuzuweisen. Mit Referenzen ist das Problem lösbar:

    @a1 = ("Maier ","Huber ","Schulze ","Schmidt ");
    @a2 = ("machen ","jeden ","Bloedsinn ","mit ");
    (@Namen,@Spruch)=(@a1,@a2);
    print @Namen;

Ein Anfänger würde erwarten, dass in das Array @Namen die Namen und in das Array @Spruch der Spruch eingelesen wird. Wir wissen jedoch, daß alles im Array @Namen landet und @Spruch leer bleibt. Referenzen können hier helfen:

    @a1 = ("Eins ","zwei ","drei ","schon ","vorbei ");
    $zeiger = \@a1;
    print $zeiger;

Eine Referenz wird also mit dem Backslash (\) gebildet. Nach der zweiten Zeile des Beispiels enthält der Skalar $zeiger eine Referenz auf die Variable @a1 bzw. auf deren Adresse. Würde man die Referenz als Skalar ausgeben lassen, könnte das Ergebnis z. B. so aussehen:

    ARRAY(0x876522c)

Der Wert in der Klammer ist die Adresse. Davor steht der Datentyp (ARRAY, SCALAR, HASH etc.). Der Zeiger zeigt auf den Speicherplatz des Rechners, wo der Array abgespeichert ist. Um auf den Inhalt der referenzierten Variablen zugreifen zu können, muß diese wieder "dereferenziert" werden. Dies geschieht, indem der Referenz der Dereferenzierer der ursprünglichen Variablen vorangestellt wird.

    @a1 = ("Eins ","zwei ","drei ","schon ","vorbei ");
    $zeiger = \@a1;
    print $$zeiger[0];

Als Ergebnis erhält man nun "Eins ".

Um auf einzelne Elemente eines referenzierten Arrays oder Hash zugreifen zu können, wird oft auch der Pfeiloperator verwendet:

    @array = (1,2,3);
    $arrayref = \@array;
    print $arrayref->[0];

Es gibt noch eine dritte Variante, die zum gleichen Ergebnis führt:

    @array = (1,2,3);
    $arrayref = \@array;
    print ${$arrayref}[0];

Alles was man mit einem Array machen kann, kann man auch mit der Referenz auf das Array machen, zum Beispiel:

    @a1 = ("Eins ","zwei ","drei ","schon ","vorbei ");
    $zeiger = \@a1;
    foreach (@$zeiger)
      {
      print "$_";
      }

Will man auf das gesamte Array zugreifen, verwendet man. @$zeiger.

Mit Hashes, Variablen und Subroutinen läuft das Verfahren genauso wie bei den Arrays ab.

    %ampel = ("oben"=>"rot","mitte"=>"gelb","unten"=>"gruen");
    $zeiger=\%ampel;
    print $zeiger;

Die Ausgabe lautet beispielsweise HASH(0x8745228) - ein Zeiger auf den Speicherplatz des Hash. Die Dereferenzierung erfolgt wie oben:

    %ampel = ("oben"=>"rot","mitte"=>"gelb","unten"=>"gruen");
    $zeiger=\%ampel;
    print $$zeiger{'oben'};

Will man auf den ganzen Hash über eine Referenz zugreifen, sieht das folgendermaßen aus:

    %ampel = ("oben"=>"rot","mitte"=>"gelb","unten"=>"gruen");
    $zeiger=\%ampel;
    foreach (keys(%$zeiger))
      {
      print "Key: $_ , Value: $$zeiger{$_} \n";
      }

Auch bei Hashes gibt es die schon bei Arrays beschriebenen Varianten des Zugriffs:

    %ampel = ("oben"=>"rot","mitte"=>"gelb","unten"=>"gruen");
    $zeiger=\%ampel;
    print "$$zeiger{'oben'}\n";
    print "$zeiger->{'mitte'}\n";
    print "${$zeiger}{'unten'}\n";

Man kann zwei Typen von Referenzen unterscheiden: Harte Referenzen und symbolische Referenzen;

#### Harte Referenzen

Harte Referenzen zeigen auf die Speicheradresse beliebiger Variablen. Um eine Referenz zu erzeugen, wird der oben erwähnte Backslash-Operator verwendet. Zum Beispiel:

    $variable = 5;
    $referenz = \$variable;
    print $$referenz;

Für Arrays und Hash-Variablen existiert eine weitere Methode, um harte Referenzen zu erzeugen:

    $arrayref = [1, 2, 'a', 'b', 'c'];
    $hashref = { 'Ort' => 'Muenchen', 'Name' => 'Meier'};

Die Besonderheit steckt hier in den Klammern, die jeweils die rechte Seite der Zuweisung umschließen. Im ersten Beispiel sind das die eckigen, im zweiten Fall die geschweiften Klammern. Durch diese Schreibweise wird eine Referenz auf ein anonymes Array bzw. einen anonymen Hash erzeugt. "Anonym" bedeutet in diesem Fall, daß kein expliziter Variablenname vergeben wird. Das Array bzw. Hash kann nur über seine Referenz angesprochen werden. Eine Anwendung für diesen Fall findet sich z. B, bei der Erzeugung von Objekten.

#### Symbolische Referenzen

Im Gegensatz zur Adresse enthalten symbolische Referenzen nur den Namen beliebiger Variablen. Bei der Referenzierung wird der Name der Variablen ohne Datentypkennzeichen der Referenz zugewiesen. Die Dereferenzierung erfolgt wie bei den harten Referenzen.

    $variable1 = 5;
    $variablenname = "variable1";
    print "$$variablenname\n";

#### Bildung mehrdimensionaler Arrays über Referenzen

    @arr = (
      ["eins","zwei","drei"],
      ["uno","due","tre"],
      ["one","two","three"]
      );
    print $arr[1][1];

In diesem Fall wird "due" ausgegeben. $arr[1] ist ein Zeiger auf ein Array, ebenso $arr[0] und $arr[2]. Vom angesprochenen Array ("uno","due"), suchen wir das Element mit dem Index 1, also "due".
Zwei weitere mögliche Schreibweisen sind (wie bereits oben beschrieben):

    @arr = (
      ["eins","zwei","drei"],
      ["uno","due","tre"],
      ["One","two","three"]
      );
    print ${$arr[0]}[1];
    print $arr[2]->[2];

Mit Hashes sieht das Ganze folgendermaßen aus:

    @arr = (
      {'Maier'=>'Kurt','Huber'=>'Erwin','Hofer'=>'Andreas'},
      {'Maier'=>'030-454567','Huber'=>'030-47301388','Hofer'=>'030-45345476'},
      {'Maier'=>'Ingenieur','Huber'=>'Politiker','Hofer'=>'Freiheitskaempfer'}
      );
    print $arr[2]{'Hofer'};

Gäbe es den Hofer nur einmal, wäre es einfach, so überlegen Sie vielleicht, was ausgegeben wird. Es wird "Freiheitskaempfer" ausgegeben. $arr[2] ist ein Zeiger auf das dritte Array, das die Berufe abspeichert. Davon wollen wir den Beruf mit dem Schlüssel "Hofer" haben.

Komplizierter wird es, wenn man Arrays und Hashes mischt und dann später alle Werte innerhalb einer Schleife auslesen will. Da Arrays anders dereferenziert werden als Hashes, muß man herausfinden können, auf was für ein Objekt der Zeiger eigentlich zeigt. Dazu gibt es die Funktion ref:

    @arr = (
       {"Goethe"=>"1749","Schiller"=>"1759","Mann"=>"1875"},
       ["Faust","Wallenstein","Buddenbrooks"],
       {"Goethe"=>"1833","Schiller"=>"1805","Mann"=>"1956"},
       ["Frankfurt","Ludwigsburg","Lübeck"]
       );
    print "ref($arr[0]), ref($arr[1]), ref($arr[2]), ref($arr[3])\n";

Ausgabe:

    HASH, ARRAY, HASH, ARRAY

Beispielhaft soll hier einmal das ganze Array @arr ausgelesen werden:

    @arr = (
       {"Goethe"=>"1749","Schiller"=>"1759","Mann"=>"1875"},
       ["Faust","Wallenstein","Buddenbrooks"],
       );
    foreach $i(@arr)
      {
      if(ref($i) eq 'HASH')
        {
        foreach (keys(%$i))
          { print "$_ wurde ${$i}{$_} geboren.\n"; }
        }
      if(ref($i) eq 'ARRAY')
        {
        foreach (@$i)
          { print "$_ ist ein wichtiges Werk der Literatur.\n"; }
        }
      }

Auch Referenzen auf Unterprogramme sind möglich:

    sub hallo
      {
      my $wer = shift;
      return "Hello, $wer !"
      }

    $ref_sub = \&hallo;
    print &$ref_sub('World'), "\n";

Bei der Bildung der Referenz ist darauf zu achten, daß sie durch Voranstellen der Zeichen "\&" vor den Namen des Unterprogramms erfolgt. Es dürfen dabei keine Klammern oder gar Parameter angehängt werden (in diesem Falle würde eine Referenz auf den Rückgabewert des Unterprogramms gebildet werden). Auch darf das "&" nicht weggelassen werden. Analog zur Dereferenzierung bei Variablen muß der Typ des Objektes durch Angabe des entsprechenden Symbols (hier: "&") festgelegt werden.

Will man ein Unterpogramm nur über eine Referenz aufrufen, bietet sich eine "anonyme Subroutine" an. Sie besitzt keinen eigenen Namen und liefert eine Referenz auf den Programmcode zurück, zum Beispiel:

    $ref_sub = sub
       {
       my $wer = shift;
       return "Hello, $wer !"
       };

    print &$ref_sub('Welt'), "\n";

Übersehen Sie nicht, daß es sich hierbei um eine Zuweisung handelt, die durch ein Semikolon abgeschlossen werden muß (es sei denn, diese Zeile steht beispielsweise am Ende eines Blockes).

Ein besonderer Fall in diesem Zusammenhang sind sogenannte "Closures", Unterprogramme mit lokalen Variablen (deklariert durch `my`). Die genannten lokalen Variablen werden bei der Definition und Zuweisung zu einer Referenz eingeschlossen.

Beispiel:

    sub hallo
      {
      my $param = shift;
      my $ref = sub { print "Hello, $param !\n" };
      return($ref);
      }

    $hans = hallo('Hans');
    $grete = hallo('Grete');

    &$hans;
    &$grete;

#### Array of Hashes

Mit Perl sind auch komplexere Datenstrukturen möglich, so auch Reihungen von Hashes. Das folgende Beispiel speichert Daten über bestimmte Dateien in einem Array. Für jede Datei werden Name, Länge und eine Beschreibung gespeichert:

    my @dateiliste = (
           {file => 'test1.zip',  length  => '764321',  desc  => 'Der 1. Test'},
           {file => 'test2.zip',  length  => '120045',  desc  => 'Der 2. Test'},
           {file => 'test3.zip',  length  =>  '67899',  desc  => 'Der 3. Test'},
           {file => 'test4.zip',  length  =>  '12300',  desc  => 'Der 4. Test'},
      );

    # Anzahl der Arrayelemente bestimmen:
    my $file_no = scalar (@dateiliste);

    # Schleife zur Ausgabe aller Dateinamen:
    for (my $i=0; $i < $file_no; $i++)
      {
      print '$dateiliste[$i]{'file'}  is:'. $dateiliste[$i]{'file'}."\n";
      }
    print "\n";

    # Schleife zur Ausgabe aller Dateilaengen:
    for (my $i=0; $i < $file_no; $i++)
      {
      print '$dateiliste[$i]{'length'} is:'. $dateiliste[$i]{'length'}."\n";
      }
    print "\n";

    # Schleife zur Ausgabe aller Dateibeschreibungen:
    for (my $i=0; $i < $file_no; $i++)
      {
      print '$dateiliste[$i]{'desc'} is:'. $dateiliste[$i]{'desc'}."\n";
      }

Das Programm erzeugt folgende Ausgabe:

    $dateiliste[0]{'file'} is: test1.zip
    $dateiliste[1]{'file'} is: test2.zip
    $dateiliste[2]{'file'} is: test3.zip
    $dateiliste[3]{'file'} is: test4.zip

    $dateiliste[0]{'length'} is: 764321
    $dateiliste[1]{'length'} is: 120045
    $dateiliste[2]{'length'} is: 67899
    $dateiliste[3]{'length'} is: 12300

    $dateiliste[0]{'desc'} is: Der 1. Test
    $dateiliste[1]{'desc'} is: Der 2. Test
    $dateiliste[2]{'desc'} is: Der 3. Test
    $dateiliste[3]{'desc'} is: Der 4. Test

Jedes Arrayelement ist so strukturiert und erlaubt den Zugriff auf einzelne Komponenten. Ebenso kann nach einzelnen Komponenten gesucht werden.

#### Hash of Arrays

Dies ist ein Hash, der als Werte hinter einem Schlüssel ein Array enthält. Er wird folgendermaßen erzeugt:

    my %Data = (
     	key1 => ['Dies', 'ist', 'das'],
     	key2 => ['Haus', 'von'],
     	key3 => ['Ni-', 'ko-', 'laus']
     );

Somit ist der Rückgabewert des Hashes für jeden Schlüssel ein Array. Auf einzelne Elemente greift man z. B. mittels `print $Data{key3}[2];` zu. Als Ausgabe würden SIe "laus" erhalten.

Um jedes Element dieses Konstruktes zu durchlaufen kann man folgende Schleifen verwenden:

    foreach $key(keys %Data)
      {
      print @{ $Data{$key} }, "\n";
      }

Das geht auch mit mit Indexen:

    foreach $key(keys %Data)
      {
      foreach $i (0 .. $#{ $Data{$key} })
        {
        print $Data{$key}[$i], "\n";
        }
      }

Sie können natürlich auch weitere Komponenten an ein Array innerhalb eines Hashes hinzufügen: `push @{$Data{"key2"}}, "Hallo", "Sieda!";`.

#### Hash of Hashes

Auch diese Form der Datenstruktur wird in manchen Programmen verwendet. Gegenüber dem Array von Hashes ändert sich eigentlich nur die Inddizierungsmethode der Komponenten. Für das Beispiel wird jetzt der Dateiname (ohne Endung) zum Indizierungskriterium.

    my %dateiliste = (
           'test1' => {file => 'test1.zip',  length  => '764321',  desc  => 'Der 1. Test'},
           'test2' => {file => 'test2.zip',  length  => '120045',  desc  => 'Der 2. Test'},
           'test3' => {file => 'test3.zip',  length  =>  '67899',  desc  => 'Der 3. Test'},
           'test4' => {file => 'test4.zip',  length  =>  '12300',  desc  => 'Der 4. Test'},
      );

    # Anzahl der Hashkomponenten bestimmen:
    my $file_no = scalar (keys(%dateiliste));

Um beispielsweise Daten zum Element `test2` zu erhalten, schreibt man:

    print $dateiliste{'test2'}->{'file'}, ": ", $dateiliste{'test2'}->{'desc'},"\n";

Auf die gleiche Weise kann man auch einzelne Werte ändern oder auch ein neues Element hinzufügen:

    $dateiliste{'test5'} = {'file' => 'test5.zip', 'length' => '0', 'desc' => 'Leerzip'};

Ebenso wäre es möglich, weitere Hash-Elemente hinzuzufügen:

    $dateiliste{'test5'}->{'owner'} = 'meier';

### 2.10 Module

Module sind eigentlich nichts anderes als Perl-Programme, die in der Regel nicht eigenständig ausgeführt werden. Alle in einem Modul deklarierten Variablen und Funktionen stehen auch dem aufrufenden Programm zur Verfügung. Daher braucht man beispielsweise in einem Perl-Programm, das als CGI-Skript ausgeführt werden soll, am Anfang nur die Zeile `use CGI;` zu schreiben, und schon steht der volle Umfang der CGI-Bibliothek bereit. Welche Funktionen bereitgestellt werden, ist der Dokumentation des jeweiligen Moduls zu entnehmen. Jede Perl-Installation wird mit einer Standardbibliothek von Modulen ausgeliefert, die bei ordnungsgemäßer Installation automatisch benutzbar ist.

Die im CPAN gespeicherten Module sind in folgende Kategorien unterteilt:

Perl Core Modules
  Standard-Module.
Development Support
  Entwicklungsunterstützung
Operating System Interfaces
  Schnittstellen zu Betriebssystemen
Networking Devices IPC
  Netzwerkfähigkeiten
Data Type Utilities
  Routinen zur Datentypwandlung
Database Interfaces
  Schnittstellen zu Datenbanken
User Interfaces
  Hier findet sich u. a. Tkperl, eine Perl-Version mit eingebauten Tk/Tcl-Routinen.
Language Interfaces
  Schnittstellen zu anderen Programmiersprachen
File Names Systems Locking
  Dateinamenverwaltung
String Lang Text Proc
  Zeichenkettenverarbeitung
Opt Arg Param Proc
  Erweiterungen zur Verwaltung von Kommandozeile, Umgebungen etc.
Internationalization Locale
  Anpassung an Fremdsprachen
Security and Encryption
  Datensicherheit und Verschlüsselung
World Wide Web HTML HTTP CGI
  Alles zur Bearbeitung von HTML-Daten
Server and Daemon Utilities
  (Der Name sagt es)
Archiving and Compression
  Archivierung und Datenkompression
Images Pixmaps Bitmaps
  Alles zur pixelweisen Bildbearbeitung
Mail and Usenet News
  Alles zum Thema Post und Nachrichten
Control Flow Utilities
  Routinen für die Ablaufkontrolle
File Handle Input Output
  Datei-Handles, Ein- und Ausgabe
Microsoft Windows Modules
  Spezialisierte Module für Windows
Miscellaneous Modules
  Verschiedenes
Commercial Software Interfaces
  Schnittstellen zu kommerzieller Software
Not In Modulelist
  Alles andere

Die Installation neuer Module kann automatisiert oder manuell erfolgen. Wenn der Computer Netzanbindung hat, das gewünschte Modul bei CPAN vorliegt und am Modul keine Änderungen vorgenommen werden sollen, empfiehlt sich die automatisierte Methode, ansonsten kann immer noch manuell verfahren werden.

- Automatisches Installationsverfahren
  Perl ist in der Lage, bei CPAN vorliegende Module automatisch zu holen, zu entpacken und zu installieren. Das Perl-Modul CPAN (in der Standardinstallation enthalten) kann mit folgendem Aufruf beim Start von Perl eingebunden werden:

      $ perl -MCPAN -e shell

  Bei diesem Aufruf stellt Perl normalerweise eine interaktive Eingabeaufforderung (`-e shell`) bereit. Erfolgt der Aufruf zum ersten Mal, werden verschiedene Parameter abgefragt und nach Vorgabe bzw. Benutzervorschlag gesetzt. Anschließend kann man dann einen CPAN-Spiegel angeben, von dem das Programm in Zukunft Module anfordern soll. Bei jedem späteren Aufruf steht sofort ein Kommandoprompt bereit, an dem man `i Modulname` eingeben kann, um das Modul "Modulname" zu installieren. Es wird dann auch automatisch die passende Versionsnummer ausgesucht. Wurde das Modul gefunden, wird es heruntergeladen, entpackt und kompiliert. Anschließend kann es sofort verwendet werden. Werden Abhängigkeiten mit weiteren Modulen festgestellt, so werden auch diese automatisch installiert.
- Manuelles Installationsverfahren
  Zunächst wird das Archiv des Moduls in ein frei wählbares Verzeichnis kopiert und das komprimierte tar-Archiv mit dem Modul mit dem Befehl `tar fxvz MyModule-0.93.tar.gz` entpackt. Es entsteht ein neues Unterverzeichnis, das in der Regel eine `README`-Datei und eine Datei namens `Makefile.PL` enthält. Zunächst ist in das neue Unterverzeichnis zu wechseln und die `README`-Datei zu lesen. Sie enthält oft Hinweise auf Mindestanforderungen der benötigten Software (Perl-Versionsnummern, benötigte andere Perl-Module und ihre Versionsnummern, externe Entwicklungsbibliotheken etc.). Bei größeren Modulen finden sich hier auch maschinenspezifische Hinweise für Compiler, Pfade etc. Dann kann die Installation mit folgenden Schritten vollzogen werden:

      perl Makefile.PL
      make
      make test
      make install

### 2.11 der Perl-Debugger

Viele verdammen Debugger als Teufelszeug, andere, wie Linus Torvalds, brauchen einfach nie einen. Oft erweisen sie sich aber als letzte Rettung bei Denkblockaden. Perl hat seinen Debugger sogar eingebaut. Aber Vorsicht! Zu leichtfertig läßt sich schlecht durchdachter Code mit einem Debugger geradeziehen, was die Software schwer wart- oder erweiterbar macht.

Manchmal verbirgt sich der Fehler aber (scheinbar) nicht im eigenen Code, sondern in einem Fremd-Modul. Manchmal ist aber auch die Dokumentation mißverständlich und man hat nur die falsche Parameterversorgung programmiert. Oder der Code des Vorgängers ist zu kompliziert, um seinen Ablauf durch Studieren der Listings zu verstehen. Perls Debugger kreist die Fehler recht schnell mittels Breakpoints, Actions und Watchpoints ein.

Das folgende Listing zeigt ein Programm zu Fakultätsberechnung, das irgendwie nicht so will - es endet nicht.

    #!/usr/bin/perl
    my $x = <>;
    my $y = fak($x);

    print "$x  $y  \n";
    exit(0);

    sub fak
      {
      my $p = shift;
      return ($p * fak($p - 1));
      }

Soll das Skript im Debugger laufen, setzt man einfach ein "perl -d" vor den vollständigen Skriptpfad und alle Kommandozeilenargumente, also "perl -d fak.pl". Das führt zu folgender Ausgabe:

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

    Enter h or `h h' for help.

    main::(fak.pl:2): my $x = <>;
    DB<1> n
    5
    main::(fak.pl:3): my $y = fak($x);
    DB<1>

Das Debugger-Kommando "n" (Next) führt die erste Zeile des Skripts aus und es kann der Wert 5 eingegeben werden (siehe Ende der obigen Ausgabe). Statt die folgenden Zeilen mit "n" vollständig auszuführen, wird nun das Kommando "s" (Step) verwendet. Prompt steigt der Debugger in die Funktion "fak" hinab (wir steppen gleich zweimal):

    DB<1> s
    main::fak(fak.pl:10):       my $p = shift;
    DB<1> s
    main::fak(fak.pl:11):       return ($p * fak($p - 1));
    DB<1>

Das Kommando "l" (List) verschafft einen Überblick über die nächsten Zeilen:

    DB<1> l
    11==>     return ($p * fak($p - 1));
    12        }
    13

Um im Code weiter nach unten zu gehen, ohne ihn auszuführen, genügt ein weiteres "l"-Kommando. Alternativ auch "l 70+20" (20 Zeilen ab Zeile 70) oder "l 70-100" (Zeilen 70 bis 100). Die nächste ausführbare Zeile zeigt "==>" an.

Durch die Eingabe eines Punktes kehrt die List-Anzeige wieder zum Ausgangspunkt zurück. Die Eingabe von "r" (Return) weist den Debugger dazu an, die aktuelle Funktion bis zum Ende auszuführen und anschließend im Hauptprogramm anzuhalten. Wir steppen aber noch etwas weiter und lassen uns mit dem "p"-Befehl den Inhalt der Variablen "$p" ausgeben.

    DB<2> s
    main::fak(fak.pl:11):       return ($p * fak($p - 1));
    DB<2> p $p
    3
    DB<3> s
    main::fak(fak.pl:10):       my $p = shift;
    DB<3> s
    main::fak(fak.pl:11):       return ($p * fak($p - 1));
    DB<3> p $p
    2
    DB<4> s
    main::fak(fak.pl:10):       my $p = shift;
    DB<4> s
    main::fak(fak.pl:11):       return ($p * fak($p - 1));
    DB<4> s
    main::fak(fak.pl:10):       my $p = shift;
    DB<4> s
    main::fak(fak.pl:11):       return ($p * fak($p - 1));
    DB<4> p $p
    0
    DB<5> s
    main::fak(fak.pl:10):       my $p = shift;
    DB<5> s
    main::fak(fak.pl:11):       return ($p * fak($p - 1));
    DB<5>

Mit jeder Ausgabeanweisung erhöht sich auch die Nummer im Prompt. Mit "H" läßt sich eine History-Liste ausgeben. Der Fehler wird oben auch schon sichtbar: Es gibt kein Abbruchkriterium! Man kann im Debugger auch Variablen neu definieren oder Variableninhalte ändern - und natürlich wieder ausgeben. Bei Hashes ist der Befehl "x" günstiger als "p".

    DB<5> %h = ("eins" => "1", "zwei" => "2");

    DB<6> p %h
    zwei2eins1
    DB<7> x %h
    0  'zwei'
    1  2
    2  'eins'
    3  1
    DB<8>

Um zum Beispiel "$p" nochmals auszugeben, genügt ein Ausrufezeichen gefolgt von der Nummer des History-Eintrags, z.B. "!2". Nun wird das Programm geändert und nochmals durchgesteppt:

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

    Enter h or `h h' for help.

    main::(fak.pl:2): my $x = <>;
    DB<1> s
    4
    main::(fak.pl:3): my $y = fak($x);
    DB<1> s
    main::fak(fak.pl:10):       my $p = shift;
    DB<1> s
    main::fak(fak.pl:11):       return (1) if ($p == 0);
    DB<1> s
    main::fak(fak.pl:12):       return ($p * fak($p - 1));
    DB<1> s
    main::fak(fak.pl:10):       my $p = shift;
    DB<1> s

       ...

    DB<1> s
    main::fak(fak.pl:11):       return (1) if ($p == 0);
    DB<1> s
    main::(fak.pl:5): print "$x  $y  \n";
    DB<1>
    4
      24
    main::(fak.pl:6): exit(0);
    DB<1> s
    DB::fake::(/usr/lib/perl5db.pl:2084):
    2084:     "Debugged program terminated.  Use `q' to quit or `R' to restart
    DB<1>

Der Perl-Debugger kann aber noch mehr. Meist ist das Abarbeiten des Programms Befehl für Befehl zu langweilig bzw. zu langwierig. Deshalb nutzt man meist Breakpoints oder Watchpoints. Als Beispiel soll das folgende Programm dienen, das ein Verzeichnis rekursiv durchläuft:

    #!/usr/bin/perl

    my $path = $ARGV[0];
    &scan_files($path);
    print "\n";
    exit(0);

    sub scan_files
      {
      my (@scandirs,$scandir,@files,$file,$list);
      $scandir = $_[0];
      opendir(DIR,$scandir) || warn "can't open the directory $scandir: $!\n";
      @scandirs = grep {!(/^\./) && -d "$scandir/$_"} readdir(DIR);
      rewinddir(DIR);
      @files=grep {!(/^\./) && -f "$scandir/$_"} readdir(DIR);
      closedir (DIR);
      for $list(0..$#scandirs)
        { &scan_files($scandir."/".$scandirs[$list]); }
      if ($#files >= 0)
        {
        print "<b>$scandir</b> contains the following files:\n";
        foreach $file(@files)
          { print $scandir."/".$file, "\n"; }
        }
      return 1;
      }

Zuerst suchen wir uns die passende Stelle für den Breakpoint, die Zeile 21. Dann wird mit "b 21" der Breakpoint gesetzt und mit "c" (Continue) das Programm gestartet. Am Breakpoint wird gestoppt und man kann sich beispielsweise die Variableninhalte ausgeben.

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

    Enter h or `h h' for help.

    main::(scandir.pl:5):   my $path = $ARGV[0];
    DB<1> l 18-22
    18:       @scandirs = grep {!(/^\./) && -d "$scandir/$_"} readdir(DIR);
    19:       rewinddir(DIR);
    20:       @files=grep {!(/^\./) && -f "$scandir/$_"} readdir(DIR);
    21:       closedir (DIR);
    22:       for $list(0..$#scandirs)
    DB<2>
    DB<2> b 21
    DB<3> c
    main::scan_files(scandir.pl:21):          closedir (DIR);
    DB<3> p @files
    wid-iec.htmlscandir.plfak.plfly1_54_gif_create.zip
    plz.perl-1.6.prog.tar.gzplz.perl-1.6.data.tar.gz
    DB<4> c
    main::scan_files(scandir.pl:21):          closedir (DIR);
    DB<4> p @files
    format.cssomega.gifshow_ads468_60_g.gifads.html
    DB<5> c
    main::scan_files(scandir.pl:21):          closedir (DIR);
    DB<5> p @files
    DB<6> c
    <b>./wid_iec</b> contains the following files:
    ./wid_iec/format.css
    ./wid_iec/omega.gif
    <b>.</b> contains the following files:
    ./wid-iec.html
    ./scandir.pl
    ./test.pl
    ./fly1_54_gif_create.zip
    ./plz.perl-1.6.prog.tar.gz
    ./plz.perl-1.6.data.tar.gz

    DB::fake::(/usr/lib/perl5db.pl:2084):
    2084:     "Debugged program terminated.  Use `q' to quit or `R' to restart.";

Statt erst den Breakpoint zu setzen und mit "c" dorthin zu fahren, hätte "c 21" gleich losgelegt und in Zeile 21 gestoppt. Dann wäre dort aber kein permanenter Breakpoint gesetzt worden, der sich später wieder verwenden lässt. Statt nun jedesmal von Hand die Variablen zu listen, hätte man auch eine Aktion definieren können.

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

    Enter h or `h h' for help.

    main::(scandir.pl:5):   my $path = $ARGV[0];
    DB<1> a 21 print "@files\n";
    DB<2> b 21
    DB<3> c
    main::scan_files(scandir.pl:21):          closedir (DIR);
    wid-iec.html scandir.pl fak.pl fly1_54_gif_create.zip
    plz.perl-1.6.prog.tar.gz plz.perl-1.6.data.tar.gz
    DB<3>
    ...

Man kann beim Breakpoint auch noch eine Aktion (meist eine Bedingung) hinzufügen, z. B.: b 55 $code eq "geheim". An diesem Breakpoint hält der Debugger nur an, wenn die Variable "$code" den String "geheim" enthält. Ist diese Bedingung erfüllt stoppt das Programm am Breakpoint und der Debugger zeigt den Inhalt der Zeile 55.

SIe haben schon gesehen, daß man mit "p" Variableninhalte ausgeben kann - wenn auch manchmal nicht sehr übersichtlich. Deshalb kennt der Debugger noch andere Befehle. "X" gibt alle Variablen im aktuellen Paket aus. Da viele dieser Variablen perlspezifisch sind (z.B. @_, $_ , $1, usw.), kann die Liste ziemlich lang werden. "X" zusammen mit einem Variablennamen gibt alle Variablen aus, die mit dem Suchnamen übereinstimmen. Geben Sie nur den Namen selbst an und nicht die Präfixe ($, @ oder %). "X foo" gibt beispielsweise 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 Befehl X birgt das Problem, dass lokale Variablen innerhalb von Subroutinen 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<4> x $input
    0  'Helle World'

Wenn Sie Arrays oder Hashes ausgeben, wird deren Inhalt 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<5> X %haschmich
    %haschmich = (
       '1' => 'rot'
       '2' => 'gelb'
       '3' => 'gruen'
    )

#### Tracing

Mit dem Kommando "t" wird der Tracemodus ein- oder ausgeschaltet. Der Debugger zeigt dann jede durchlaufene Zeile an. Als Beispiel diene wieder das erste Programm (Fakultätsberechnung):

    main::(fak.pl:2): my $x = <>;
    DB<1> t
    Trace = on
    DB<1> c
    3
    main::(fak.pl:3): my $y = fak($x);
    main::fak(fak.pl:10):       my $p = shift;
    main::fak(fak.pl:11):       return (1) if ($p == 0);
    main::fak(fak.pl:12):       return ($p * fak($p - 1));
    main::fak(fak.pl:10):       my $p = shift;
    main::fak(fak.pl:11):       return (1) if ($p == 0);
    main::fak(fak.pl:12):       return ($p * fak($p - 1));
    main::fak(fak.pl:10):       my $p = shift;
    main::fak(fak.pl:11):       return (1) if ($p == 0);
    main::fak(fak.pl:12):       return ($p * fak($p - 1));
    main::fak(fak.pl:10):       my $p = shift;
    main::fak(fak.pl:11):       return (1) if ($p == 0);
    main::(fak.pl:5): print "$x  $y  \n";
    3
      6
    main::(fak.pl:6): exit(0);
    DB::fake::(/usr/lib/perl5db.pl:2084):
    2084:     "Debugged program terminated.  Use `q' to quit or `R' to restart.";

Zum Abschluss noch ein kleiner Trick, um ein Programm jede ausgeführte Zeile anzeigen zu lassen. Dieses Tracing lässt sich über die Environment-Variable "PERLDB_OPTS" einstellen, bevor der Aufruf des Debuggers erfolgt:

    PERLDB_OPTS="NonStop=1 AutoTrace=1 frame=2" perl -d Programm

Die "AutoTrace"-Option setzt den Debugger in den Tracing-Modus, indem er jede Sourcezeile erst ausgibt, bevor er sie ausführt. Mit der "NonStop"-Option hält der Debugger weder am Anfang noch am Ende an. Mit "frame=2" kommen beim Eintritt und beim Verlassen von Unterfunktionen "entering ..."- und "exiting ..."-Meldungen dazu. Wer zusätzlich übergebene Parameter und Rückgabewerte von Unterfunktionen braucht, setzt "frame=4".

Eine Einführung in den Gebrauch des Debuggers findet sich in jeder neuen Perl-Distribution unter "perldoc perldebtut". Die ausführliche Dokumentation steht in "perldebug".

#### Debugger-Befehle

Kommando | Bedeutung
--- | ---
Programmausführung steuern
n | Nächste Zeile ausführen, danach anhalten
s | Nächste Zeile starten, in Unterfunktion anhalten
r | Aktuelle Funktion fertig durchlaufen, dann stopp
R | Zurück zum Start und noch einmal ausführen
Variablen anzeigen
p | Wert ausgeben
x | Dump (x \%hash)
V | Werte ausgeben
X | Werte ausgeben
Source-Navigation
l | Vorwärts blättern
- | Rückwärts blättern
v | Code um aktuelle Zeile herum zeigen
. | Zurück zur aktuellen Zeile
f | In eine andere Source-Datei wechseln
Erweiterte dynamische Navigation
c Zeile | Code bis zu dieser Zeile ausführen, dann stoppen
c Funktion | Code bis zur Funktion ausführen, in ihr anhalten
b Zeile | Breakpoint in Zeile setzen
b Funktion | Breakpoint in Funktion setzen
b Zeile/Funktion | Breakpoint mit Bedingung Bedingung
a Zeile/Funktion Aktion | Actionpoint in Zeile/Funktion
w Zeile/Funktion | Watchpoint in Zeile/Funktion Variable
< Command | Pre-Prompt setzen
L | Breakpoints, Watchpoints, Actions anzeigen
B/A/W | Breakpoints, Watchpoints, Actions löschen

In dieser Einführung sind bei weitem nicht alle Eingenschaften und Möglichkeiten von Perl beschrieben worden. Wer mehr wissen will, konsultiere die einschlägige Literatur.

Zum vorhergehenden Abschnitt | Zum Inhaltsverzeichnis | Zum nächsten Abschnitt

###### Copyright © Hochschule München, FK 04, Prof. Jürgen Plate
Letzte Aktualisierung: