![]() |
Grundlagen CGI-Programmierung mit Perlvon Prof. Jürgen Plate |
#!/usr/bin/perl # # Das unvermeidliche erste Programm # print 'Hello world.'; # Ausgabe eines TextesFast jedes Perl-Programm beginnt unter UNIX mit der Zeile
#!/usr/bin/perlobwohl 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.
chmod u+x prognameUm 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.
| 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. |
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".
| 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 |
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.
| 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"; |
$txt = "Hallo"; print "$txt Welt\n";
$txt = "Hallo"; print '$txt Welt\n';
| 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 ~;
print <<EOF; Die Antwort ist $answer. EOFbziehungsweise
print <<"EOF"; Die Antwort ist $answer. EOF
Achtung:
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".
$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.
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 belegtMan 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] = fuenfBeispiel: Eine Liste sortiert ausgeben, Doubletten entfernen
@list = sort(@list);
$old = "";
foreach $element (@list)
{
if ($element ne $old)
{
print $element, "\n";
}
$old = $element;
}
| 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";
%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.
| 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. |
| 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";
}
| 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=~ !~:
Gleichheitsoperatoren == != <=> eq ne cmp:
Von Interesse hier ist vor allem "<=>" (für numerische
Werte) bzw. "cmp" (für Zeichenketten):
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.
print "A"."h" x 10, "!\n"; # erzeugt die Ausgabe "Ahhhhhhhhhh!"
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:
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++;
}
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;
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:
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:
| . | 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 |
| 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:
| | | 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/ä/\ä/g; $Text =~ s/ö/\ö/g; $Text =~ s/ü/\ü/g; $Text =~ s/Ä/\Ä/g; $Text =~ s/Ö/\Ö/g; $Text =~ s/Ü/\Ü/g; $Text =~ s/ß/\ß/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.
| 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) |
([\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.
($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.
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:
Für den Aufruf der Funktion myfunc gibt es in Perl drei Möglichkeiten:
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";
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);
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:
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;
}
}
| 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:
Befehle zum Ausführen von Kommandos auf Systemebene:
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);
}
}
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:
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 runningEin 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 |
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)
$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.
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 AusgabedatenGeschrieben 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 " .... ";
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>).
| 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. |
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:
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.
| %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.
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 .
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);
}
rename("alter_name","neuer_name");
unlink("dateiname");
$permissions = 0777;
mkdir("dirname",$permissions);
rmdir("dirname");
link("filename","linkname");
symlink("filename","linkname");
$permissions = 0755; chmod($permissions,"prog_1","prog_2","prog_3");
$uid = 100; # Benutzer-ID $gid = 10; # Gruppen-ID chown($uid,$gid,"datei");
$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.
$| = 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.
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;
$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.
$variable1 = 5; $variablenname = "variable1"; print "$$variablenname\n";
@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".
@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, ARRAYBeispielhaft 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;
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.
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!";.
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';
Die im CPAN gespeicherten Module sind in folgende Kategorien unterteilt:
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.
$ perl -MCPAN -e shellBei 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.
perl Makefile.PL make make test make install
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 } 13Um 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];