Perl in der Praxis
Frank Richter, URZ, Januar 2008
Historie und Eigenschaften
Entwickelt seit Ende der 80er Jahre von Larry Wall (u.a. patch, rn)
- UNIX-Administrator: Sammlung von Shell-Skripten und umfangreichen awk-Programmen
- unzufrieden - entwickelte eigene Skriptsprache, bis er zufrieden war
Ergebnis: Perl - Practical Extraction and Report Language
- Best of C, Shell, awk, sed ...
- Veröffentlicht im Internet - enormer Zuspruch
- GNU General Public License oder Artistic License
- Die Skriptsprache von UNIX-Admins
- WWW - CGI-Programme: Perl die erste Wahl
- Schließt Lücke zwischen pragmatischer, aber oft ineffizienter/umständlicher Shell-Programmierung und performanter, aber recht umständlich handhabbarer Programmiersprache C
- für "jede" Plattform
- große Entwicklergemeinschaft
- jede Menge Literatur
- Newsgruppen comp.lang.perl, de.comp.lang.perl
- WWW-Server: www.perl.com, www.perl.org
Großer Funktionsumfang:
- Datentypen, Operatoren, Konstrukte, Funktionen, Klassen
- Quasi-Standard: Perl regular expressions (PCRE)
- kompakte, z.T. "kryptische" Notation
- häufig mehrere Wege für eine Lösung
- Module für jede Aufgabe: CPAN (Comprehensive Perl Archive Network)
Eingesetzt z.B. in
- TWiki - Wiki-Autorensystem
- OTRS - Open Ticket Request System
Konkurrenz:
- Python als moderne Skriptsprache
- PHP im WWW-Umfeld
- Ruby (on Rails)
Interpreter-Sprache:
- Zu Beginn wird gesamtes Skript eingelesen und syntaktisch geprüft.
- -> Opcode - hohe Performance
- kurzer Entwicklungszyklus: Text-Editor, Aufruf
Dokumentation und Literatur
Linux - englisch:
-
man perl
-
man perlfunc
...
-
perldoc -f print
- Hilfe zu Funktion
-
perldoc -q muster-in-FAQ
WWW - englisch:
Bücher - deutsch:
- RRZN-Handbuch: Perl - Eine Einführung (via URZ, 3 Euro)
- Ditchen, Patrick: Perl - Schritt für Schritt zum Profi (in 21 Tagen)
- Randal L. Schwartz, Tom Phoenix: Einführung in Perl (Learning Perl)
Formalitäten
- Perl-Skript = Text
- formatlos, d.h. Anzahl der Leerzeichen, Leerzeilen usw. egal
- jede Anweisung mit Semikolon abschließen
- Blöcke durch
{ ... }
-
# Kommentar bis Zeilenende - Kommentare sind sehr wichtig!
Aufruf
- Skript in Datei
hallo.pl
print "Hallo Welt!\n";
- "Shebang" - erste Zeile in
hallo.pl
#!/usr/bin/perl
print "Hallo Welt!\n";
-
chmod +x hallo.pl
- Aufruf:
% ./hallo.pl
- Von Kommandozeile:
% perl -e 'print sin(1.5)'
- Zum Test:
% perl
beliebiger Perl-Code
^D
- Weitere Kommandozeilen-Optionen
perl -v |
Version |
perl -w Skript |
Ausgabe von Warnungen - empfohlen! |
perl -c Skript |
Syntax-Prüfung |
perl -d Skript |
Debugger |
perl -h |
weitere ... |
Variablen (1)
Skalar |
genau ein Wert: Zahl, Zeichenkette |
$name |
Array |
Liste von Werten, indiziert durch ganze Zahl |
@feld |
Hash |
Liste von Werten, indiziert durch Zeichenkette (Assoziativfeld) |
%hash |
- müssen nicht zwingend deklariert und initialisiert werden - macht Perl implizit
- toll für kleine Skripte
- Initialisierung empfohlen für größere Projekte
use vars qw($zaehler @array); # Deklaration von globalen Variablen
$zaehler = 0; # Initialisierung
@array = ();
Skalare
Immer $-Zeichen: bei Wertzuweisung und bei Zugriff auf Wert.
Zahlenwerte
$int = 123; # ganze Zahl
$oktal = 040; # Oktal, = 32 dezimal
$hexadezimal = 0x42; # Hexadezimal, = 66 dezimal
$float = 4.56; # Dezimalbruch mit Punkt
$_12 = 12E-5; # Exponentialdarstellung: "12 mal 10 hoch -5" = 0.00012
Arithmetische Operationen:
- Zuweisung:
=
- Grundrechenarten:
$a = 4 + 1; $b = ($a - 2) * 3;
- Modulo
%
, Potenz **
, Wurzel: print $b ** 0.5;
- Kurzschreibweise:
$b /= 3; # $b = $b / 3;
- In-/Dekremtieren:
++$a; $b++; $c = $b--;
- Reihenfolge (Präzedenz) vgl. C, am besten Klammern setzen
Numerische Vergleiche:
- Gleich, ungleich:
$b == 3; $b - 1 != 3
- größer, kleiner (gleich):
$alter >= 18
Mathematische Funktionen:
- Winkelfunktionen:
sin($x); cos($x); atan2($x, $y);
- Exponential-, Logarithmusfunktion:
$e = exp(1); log($e);
- Konvertierung:
$i = hex(16); print int($i/7);
- Zufallszahl:
print int(rand(10)); # zwischen 0 und 9 (inkl.)
Bitoperationen (ähnlich C): print 0x10 << 2 | 2;
Viele mathematische Zusatz-Module:
- Integer-Zahlen beliebiger Länge:
use Math::BigInt;
Zeichenketten
$string1 = '456'; # Keine Substitution, außer \' = ', \\ = \
$string2 = "Hallo $string1!\n"; # Doppel-Apostroph: Variablen-Substition, \-Sonderzeichen
print "\e[38m Roter Text\e[0m\n"; # Escape-Sequenz
$html = <<END; # sog. Here-Dokument
<html><head>
<title>$string2 ...
END
Automatische Konvertierung: $summe = $int + $string; # 579
Zeichenketten-Operatoren:
- Verkettung:
$url .= '.html'; print '<a href="' . $url . '">';
- Wiederholung:
$trenner = '-' x 80;
Vergleichsoperatoren: eq
- gleich, ne
- ungleich: if ($string1 eq $string2) {...}
Vielzahl von Zeichenketten-Funktionen:
$laenge = length($trenner); # 80
print lc($string2); # Kleinschreibung, uc = Großschreibung
print ucfirst('gross und Stark'); # nur erster Buchstabve groß: Gross und Stark
chomp($zeile); # entfernt abschließendes Newline
$teil = substr('26 Juni 2007', 3, 4); # Teilzeichenkette: Juni
substr($teil, 2, 1) = 'l'; # Ersetzen: Juli
Ausgabe / Formatieren:
$fahrenheit = 20;
$celsius = ($fahrenheit - 32) * 5 / 9;
print "$fahrenheit F = $celsius C\n";
# Besser: gerundet auf eine Nachkommastelle
printf("%d F = %5.1f C\n", $fahrenheit, $celsius);
Reguläre Ausdrücke:
-
if ($string =~ /muster/) { ... }
- später ...
Spezial-Variablen, z.B.:
-
$0
- Skriptname
-
$_
- Standard-Variable, in Schleifen gesetzt, z.B. von Funktionen als Standard-Argument verwendet
-
$$
- Prozess-ID (wie in Shell)
viele weitere ...
Datei-Testoperationen
- Ähnlich
test
bzw. [ ... ]
in der Shell:
-f name |
Name ist "normale Datei" |
if (-f $tmpname) |
-d name |
Name ist Verzeichnis |
if (-d '/var/log' ) |
-r name |
Name ist lesbar |
if (-r '/var/log/messages') |
-w name |
Name ist schreibbar |
if (-w '/etc/passwd') # Oha! |
-s name |
Größe einer Datei in Bytes |
print -s '/etc/group'; |
Weitere: http://www.tu-chemnitz.de/docs/perldoc-html/functions/-X.html
Logische Operatoren
- UND- bzw. ODER-Verknüpfung, NOT bzw. XOR:
if ($a > 0 && $a <= 100) # $a zwischen 1 und 100 (inkl.), auch and
if ($s eq 'y' || $s eq 'j') # $s gleich y oder j, auch or
if (! $s) # $s ist leer oder Null
Bedingte Anweisungen: $a < 10 && print "$a kleiner 10\n";
Weitere Operatoren: http://www.tu-chemnitz.de/docs/perldoc-html/perlop.html
Steueranweisungen
Verzweigungen
if (Bedingung) {
# Anweisungen: wenn Bedingung wahr
} elsif (andere Bedingung) {
# Anweisungen: wenn andere Bedingung wahr
} else {
# sonst diese Anweisungen
}
Bedingung = logischer Ausdruck:
- alles Mögliche: Vergleichsoperation, Muster, Datei-Testoperation, Funktionsruf
- beliebig logisch verknüpft
- falscher Wert: Zahl 0, Zeichenkette '0' oder leer
- alles andere ist wahr
Bedingte Anweisung: print "$a kleiner 10\n" if ($a < 10);
Bedingungsoperator ?
- wie in C:
-
$anrede = $geschlecht eq 'm' ? 'Herr' : 'Frau';
-
$max = $a > $b ? $a : $b;
- Größere Abschnitte auskommentieren:
if (0) {
... nicht abgearbeiteter, aber interpretierter Kode - Syntax muss stimmen!
}
Schleifen
while (Bedingung) { # Solange Bedingung wahr ist
...
}
until (Bedingung) { # Solange Bedingung nicht wahr ist
...
}
do {
} while (Bedingung);
Auch hier bedingte Anweisungen:
-
print "$i. Zeile\n" while (++$i <= 5);
-
print lc($line) while $line = <STDIN>;
Zählschleife
for (Initialisierung; Abbruchbedingung; Operation) {
...
}
z.B. Zinsberechnung:
$guthaben = 1000;
for ($jahr = 1; $jahr <= 10; $jahr++) {
$guthaben += $guthaben * 0.05; # Zins 5 %
printf "Jahr %2d: %8.2f\n", $jahr, $guthaben; # runden
}
Schleife über eine Liste: foreach $var (liste) { ... }
foreach $stadt ('Leipzig', 'Dresden', 'Chemnitz') {
print "$stadt\n";
}
Sprungbefehle
- Verlassen des Programmes:
exit wert;
, mit Fehlermeldung: die "Fehler bei ...";
- Vorzeitiges Ausspringen aus einer Schleife:
last
(break
in C)
- Springen zur Schleifenbedingung:
next
(continue
in C)
- labels, goto ...
# Listet Dateien im aktuellen Verzeichnis mit Größe
foreach $file (<*>) { # file globbing liefert Datei-Liste lt. Shell-Muster: *, ?, []
next if (! -f $file); # überspringen, wenn keine normale Datei
$size = -s $file;
print "$file\t$size\n";
}
# Das Ganze sehr kompakt - die Standardvariable $_ kommt ins Spiel
foreach (<*>) { -f && print "$_\t", -s, "\n"; }
Variablen (2)
Arrays
Initialisierung:
-
@array = liste
liste = ( wert1, wert2, ... );
-
@wtage = ('Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So');
@wtage = qw(Mo Di Mi Do Fr Sa So); # identisch, qw = quoted words
-
@ziffern = (0 .. 9); # Bereichsoperator ..
-
@zeiten = split(':', '12:34:56');
($stunde, $minute, $sekunde) = split(':', '12:34:56');
- zweidimensional:
$matrix = ( ['00', '01'], ['10', '11']);
Zugriff:
-
$array[index]
- index von 0 bis $#array
-
$stunde = $zeiten[0];
-
print "Die Woche geht von $wtage[0] bis $wtage[$#wtage].\n";
-
print "Es gibt " . ($#wtage + 1) . " Wochentage.\n";
-
@wochenende = @wtage[5,6];
-
$wert = $matrix[1][0];
Funktionen:
-
pop
- entfernt das letzte Element und liefert es
-
push
- fügt ein oder mehrere Element(e) "hinten" an
-
shift
- entfernt das erste Element und liefert es
-
unshift
- fügt ein oder mehrere Element(e) "vorn" an
-
splice
- komplexe Operationen im Array
Beispiel: Argumente der Kommandozeile via @ARGV
$usage = "$0 datei1 datei2 ...";
if ($#ARGV <= 1) {
die "Aufruf: $usage\n";
}
while (@ARGV) { # alle Argumente
$datei = shift @ARGV;
# ...
}
- Array in Zeichenkette wandeln:
print join(' | ', @wtage), "\n";
- vorher alphabetisch sortieren:
print join(' | ', sort @wtage), "\n";
- oder umkehren:
print join(' | ', reverse @wtage), "\n";
- Suchen:
@mtage = grep(/M/, @wtage);
Hashes - Assoziativfelder
Initialisierung:
-
%hash = (key => value, ... )
-
%telefon = ('Meier' => '1234', 'Mueller' => '5678');
- Kopieren:
%telefon_neu = %telefon;
- Mehrfach-Hashes:
%telefon2 = ('Meier' => { 'work' => '1234', 'home' => '4321'},
'Mueller' => { 'work' => '1234', 'home' => '4321'}
);
Zugriff:
-
$hash{key}
-
print $telefon{'Mueller'}, "\n";
-
$dienst = $telefon2{'Meier'}{'work'};
- Neuer Wert:
$telefon{'Meier'} = '4321';
- Neues Element:
$telefon{'Schulze'} = '9876';
- Element löschen:
delete $telefon{'Mueller'};
Funktionen:
-
if (! defined $telefon{'Chef'}) { $telefon{'Chef'} = '1000'; }
-
keys %hash
liefert Liste aller Schlüssel
-
values %hash
liefert Liste aller Werte
Beispiel: Gib kompletten Hash aus:
foreach $name (keys %telefon) {
print "Name: $name\tTelefon: $telefon{$name}\n";
}
# Besser: sortiert, mit Überschrift
print "Name\tTelefon\n";
foreach $name (sort keys %telefon) {
print "$name\t$telefon{$name}\n";
}
# Jetzt: Sortiert nach Wert
print "Telefon\tName\n";
foreach $name (sort {$telefon{$a} cmp $telefon{$b}} keys %telefon) {
# cmp ist alphabetischer Vergleichsoperator
# <=> ist numerischer Vergleichsoperator
print "$telefon{$name}\t$name\n";
}
Zugriff auf Umgebungsvariablen via %ENV
$home = $ENV{'HOME'}; # Lesen
$ENV{'PATH'} .= ':.'; # Setzen
Referenzen (Zeiger)
$zeiger_s = \$s;
$zeiger_a = \@array;
$zeiger_h = \%hash;
$zeiger_f = \&funktion;
- Zeiger bleiben gültig, auch wenn die ursprüngliche Variable nicht mehr existiert.
if ($$zeiger_s < 0) ... # auch ${$zeiger_s}
@kopie = @$zeiger_a; # Kopie des Arrays, auf das $zeiger_a zeigt
$first = $$zeiger_a[0]; # oder $zeiger_a->[0];
$last = $$zeiger_a[$#$zeiger_a]; # oder $zeiger_a->[$#$zeiger_a];
$wert = $$zeiger_h{'key'}; # oder $zeiger_h->{'key'};
&$zeiger_f(1,2); # oder $zeiger_f->(1,2);
Umgang mit Dateien
Lesen
- Öffnen belegt "Dateizeiger", Konvention: Großbuchstabe(n):
-
open F, 'dateiname'
- öffnet dateiname zum Lesen
-
open F, '/etc/passwd' or die "Fehler beim Oeffnen: $!\n";
- Abfangen eines Fehlers, $!
ist Systemfehlermeldung
- Lesen via Dateizeiger in "Diamantoperator":
-
$zeile = <F>; # eine Zeile
-
while ($zeile = <F>) { ... } # fuer jede Zeile
-
@inhalt = <F>; # gesamte Datei in Array, Zeilen indexierbar
- Schließen: (sonst erst automatisch bei Programmende)
-
close F;
-
close F or warn "Fehler beim Schliessen: $!\n" # mit Warnung bei Fehler
- Vordefinierter Dateizeiger: <STDIN> - Eingabe (i.A. Tastatur)
-
$eingabe = <STDIN> # liest eine Zeile von Tastatur
# Datei ausgeben mit Zeilennummer:
print "Datei: ";
$datei = <STDIN>; # Abfrage via Eingabe
chomp $datei; # Newline am Ende wegschneiden
open F, $datei or die "Kann $datei nicht oeffnen: $!\n";
while (<F>) {
# Ohne Variable --> verwende Standard-Variable $_
print $., ' ', $_; # $. = aktuelle Zeilennummer
}
- Wenn Skript eine oder mehrere Datei(en), die auf der Kommandozeile angeben sind, oder Daten der Eingabe bearbeiten soll:
while ($zeile = <>) {
print lc($zeile); # alles in Kleinbuchstaben wandeln und ausgeben.
}
# Kurzform
while (<>) { # Standard-Variable $_ verwenden
print lc; # Auch lc verwendet Standard-Variable ...
}
Schreiben
- Öffnen:
-
open S, '> dateiname'
- öffnet dateiname zum (Über-)Schreiben
-
open S, '>> dateiname'
- Daten werden hinten angehangen
- Schreiben via Dateizeiger:
-
print S "eine Zeile\n"; # eine Zeile
-
printf S "%d\t%s\n", $zahl, $string;
- Schließen wie gehabt:
close S;
# Neue Datei ausgeben mit Zeilennummern:
print "Datei: ";
$datei = <STDIN>; # Abfrage via Eingabe
chomp $datei; # Newline am Ende wegschneiden
open LIES, $datei or die "Kann $datei nicht oeffnen: $!\n";
open SCHREIB, "> $datei.num" or die "Kann $datei.num nicht zum Schreiben oeffnen: $!\n";
while (<LIES>) {
print SCHREIB $., ' ', $_; # $. = aktuelle Zeilennummer
}
close SCHREIB;
close LIES;
print "Datei $datei.num geschrieben.\n";
Funktionen zum Dateisystem
mkdir dir, mode |
Verzeichnis anlegen |
-d '/tmp/test' or mkdir '/tmp/test', 0755; |
chmod mode, file |
Rechte ändern |
chmod 0750, 'dada'; |
unlink file |
Datei löschen |
unlink <*.bak>; |
viele weitere
Aufruf von externen Programmen
-
system(kommando)
führt kommando aus, mit Ausgabe, liefert exit-Status:
if (system("ls -l $dir") != 0) {
print "Fehler bei Aufruf: $?\n";
}
-
`...`
(Backtick wie Shell) oder qx(...)
gibt Kommandoausgabe zurück: $etc =`ls -l /etc`;
Pipes
Lesen:
-
open PROG, "command |" or die "Kann command nicht starten: $_\n";
Schreiben:
-
open P, "| command" or die "Kann command nicht starten: $_\n";
# Transparentes Behandeln evtl. komprimierter Dateien
if ($datei =~ /\.gz$/) { # dateiname mit .gz am Ende
open F, "zcat $datei |" or die "Kann zcat nicht starten: $!\n";
} else {
open F, $datei or die "Kann Datei nicht oeffnen: $!\n";
}
while (<F>) { ... }
close F;
Aufruf von Perl-Kode
eval Perl-Kode
- führt Perl-Kode in eigenem Interpreter aus
- z.B. zum Umgehen des Programmabbruchs bei Division durch 0
eval { $resultat = $a / $b; }; warn $@ if $@;
- "Breakpunkt" während Entwicklung:
eval <STDIN>;
# Mini-Taschenrechner
do {
print "Rechner: ";
chomp($in = <STDIN>);
$res = eval $in;
if ($@) { # Fehler von eval
print "Ungueltige Eingabe\n";
} else {
print "Ergebnis: $res\n";
}
} while ($in);
Funktionen
Definieren:
-
sub funktionsname { ... }
- Argumente via
@_
- Standard-Array
- Rückgabe via
return value
- Lokale Variablen:
my $var;
- Rekursion möglich
sub summe2 {
my ($a, $b) = @_;
return $a + $b;
}
sub summe { # beliebige Anzahl der Argumente
my $s = 0;
foreach (@_) { $s += $_; }
return $s;
}
Aufruf:
-
&funktionsname(argumente ...);
- neuerdings auch ohne &
- Reihenfolge Definition / Aufruf egal
print summe(1..1000), "\n";
Argumente per Referenz (Zeiger):
@grossdatei = <F>; # gesamte Datei in array
&work(\@grossdatei);
sub work {
my ($aref) = $@;
print $aref->[0]; ...
}
Signalbehandlung:
- Signal (z.B. durch Drücken von
^C
) an ein Perl-Skript während der Ausführung führt normalerweise zum Abbruch.
- Dies kann gesteuert werden, entweder um manche Signale zu ignorieren oder um vorm Abbruch noch Aktionen auszuführen.
- Hash
%SIG
enthält als Schlüssel die Namen der Signale, z.B. INT
für Interrupt-Signal (^C
).
Als Werte sind möglich:
-
DEFAULT
= Abbruch
-
IGNORE
= Kein Abbruch, Signal wird nicht beachtet
-
\&func
= funktion func
wird aufgerufen.
# Bei ^C soll eine temporäre Datei gelöscht werden, danach wird beendet.
$SIG{'INT'} = \&tmpdatei_loeschen;
...
sub tmpdatei_loeschen {
unlink '/tmp/temp_datei';
exit;
}
Reguläre Ausdrücke
- beschreiben Muster von Zeichenketten, z.B. "Eine ID-nummer und eine Beschreibung" -> "vier Ziffern, gefolgt von beliebig vielen Leerzeichen, danach beliebige Zeichen außer Ziffern"
- Zum Filtern (und Ersetzen) von bestimmten Daten aus einem "Datenstrom"
- auch in anderen Linux-Programmen:
awk, sed, grep, vim, ...
Bindungs-, Matchoperator und Ersetzungsoperator:
-
$string =~ m/reg. Ausdruck/;
- Wenn "Trenner" / ist, kann
m
auch weggelassen werden, mit m
beliebiger Trenner möglich
-
$string =~ s/reg. Ausdruck/Ersetzung/;
- auch hier: beliebige Trenner
print "Abbruch? ";
chomp($eingabe = <STDIN>);
exit if ($eingabe =~ /j/); # eingabe enthält j
if ($dateiname !~ m#/#) { ... } # dateiname enthält KEIN /
while (<>) {
print if (/Subject:/); # aktuelle Zeile enthält Subject:
}
Aufbau von Mustern
"Joker" für einzelne Zeichen:
Muster-Zeichen |
Bedeutung |
Beispiel |
Erläuterung |
. |
ein beliebiges Zeichen |
/M.st/ |
Mast, Mist, Most, aber auch M8st, aber nicht Morast |
[abc] oder [0-9] |
ein Zeichen aus einer Gruppe |
/M[aio]st/ |
Mast, Mist, Most |
[^A-Z] |
ein Zeichen nicht aus dieser Gruppe |
/M[^a]st/ |
nicht Mast |
\d bzw. \D |
eine Ziffer bzw. ein Zeichen, aber keine Ziffer |
/ \d\d:\d\d / |
"Leer, 2 Ziffern, :, 2 Ziffern, Leer" |
\w bzw. \W |
ein Wortzeichen [0-9A-z_], bzw. kein Wortzeichen |
|
|
\s bzw. \S |
ein Leerzeichen [ \t\n\f\f] bzw. kein Leerzeichen |
|
|
\ |
hebt Sonderzeichen auf |
/datei\.html/ |
Sonderbedeutung von . aufgehoben |
Quantifizierer - wie oft ein Zeichen oder eine Zeichenklasse?
Muster-Zeichen |
Bedeutung |
Beispiel |
Erläuterung |
* |
vorheriges Zeichen/Muster beliebig oft, auch gar nicht |
/M.*st/ |
Mast, ..., auch Morast, aber auch Mst |
+ |
vorheriges Zeichen/Muster mindestens einmal |
/\d+\s+/ |
eine Zahl, gefolgt von mind. einem Leerzeichen |
? |
vorheriges Zeichen/Muster einmal oder gar nicht |
/Jan 0?\d/ |
Datum vom 1. bis 9. Januar, egal ob führende 0 |
{n} |
vorheriges Zeichen/Muster genau n Mal |
/ \d{4} / |
eine vierstellige Zahl in Leerzeichen |
{m,n} |
vorheriges Zeichen/Muster m bis n Mal |
/ [a-z]{3,8} / |
drei- bis achtstellige Kleinbuchstabenfolge |
{m,} |
vorheriges Zeichen/Muster mindestens m Mal |
/ \w{8,} / |
Wörter mit mind. 8 Zeichen |
- matchen normalerweise "gefräßig" (greedy), so viele Zeichen, wie sie ins Muster passen
- minimales Matchen durch Anhängen von
?
an Quantifizierer
$htmltitel = '<title>Perl-Zauberei</title>';
$text = $text1 = $text2 = $htmltitel;
$text =~ s/<.+>//; # ersetze alles zwischen < und > durch nichts
$text1 =~ s/<.+?>//; # das gleiche, nur "nicht gefräßig"
$text2 =~ s/<.+?>//g; # das gleiche, nur "nicht gefräßig" und beliebig oft
print "$text\n$text1\n$text2\n";
Verankerung:
Muster-Zeichen |
Bedeutung |
Beispiel |
Erläuterung |
^ |
Anfang der Zeichenkette |
m#^/# |
beginnt mit / |
$ |
Ende der Zeichenkette |
/\.html$/ |
endet mit .html |
\b bzw. \B |
Wortgrenze (Anfang oder ende) bzw. keine Wortgrenze |
/\bftp\b/ |
"Wort" ftp, aber nicht Luftpumpe |
Gruppierung, Alternativen, Variablen:
Muster-Zeichen |
Bedeutung |
Beispiel |
Erläuterung |
(muster) |
Zeichen gruppieren |
/(\d+\.\d+;){5}/ |
fünf durch Semikolon getrennte Kommazahlen |
(muster1|muster2) |
Alternativen |
/(Jan|Feb|Mar)/ |
Jan, Feb oder Mar |
$var bzw. ${var} |
Variable, wird ersetzt durch Wert |
|
|
Quotierung - Sonderzeichen flüchten:
- Bei allen Sonderzeichen, die als normale Zeichen behandelt werden sollen, muss
\
vorangestellt werden:
if ($var =~ /\[\w{3}\]/) # [...]
- Bei vielen:
$klammern = quotemeta '()[]{}'; if ($ausdruck =~ /$klammern/) ...
- Bei Verwendung von Variablen kann regulärer Ausdruck ungültig werden - hier flüchten mit
\Q...\E
:
if ($zeile =~ /\Q$eingabe\E/) ...
Rückbezug (Backreferencing)
Auf durch Klammern zusammengefasste Teile kann wieder Bezug genommen werden: (muster)...\1
- im regulären Ausdruck:
/(muster)...\1/
, z.B. /(\d)\1/
passt auf 11, 22, ...
- beim Ersetzen:
s/...(muster).../$1/
, z.B.
$datum =~ s/\b([A-Z][a-z]{2}) (\d{2})\b/$2. $1/; # Datum umformen: Jan 13 -> 13. Jan
- später
/...(muster).../; $var = $1;
, z.B. if ($zeile =~ /\b([A-Z][a-z]{2})\b/) { $monat = $1; }
# Palindrome mit vier Buchstaben, z.B. ANNA
if (open(F, '/usr/share/dict/words')) {
while (<F>) {
$l = lc; # Zeile in Kleinbuchstaben
print if ($l =~ /^(\w)(\w)\2\1$/); # zwei Zeichen, dann 2. Zeichen und wieder 1. Zeichen
}
}
Modifizierer
- beeinflussen das Verhalten
- werden hinten angefügt, hinter das letzte Trennzeichen, z.B.
if (/error/i) { ... }
- mehrere Modifizierer einfach hintereinander schreiben
Modifizierer |
Bedeutung |
Beispiel |
Erläuterung |
g |
global, d.h. suche/ersetze alle Vorkommen des Musters |
$a = 'abba'; $a =~ s/a/e/g; |
-> ebbe, ohne g: ebba |
i |
ignoriere Groß-/Kleinschreibung |
if (/error/i) |
findet error, Error, ERROR, ... |
s |
single line, . passt auch auf \n |
|
|
m |
multi-line, ^ und $ passen auf Beginn/Ende jeder Zeile |
|
|
e |
eval - nur bei Ersetzen: der Ersetzungsteil wird als Perl-Ausdruck ausgewertet |
s/(\d+)/$1*10/ge; |
Ersetze Zahlen durch ihr Zehnfaches |
x |
extended - Kommentare und Leerzeichen/Zeilumbrüche im Muster erlaubt |
s/(\d+) # Zahl/$1*10/gex; |
|
Beispiel: Wörter zählen
while (<>) { # Für jede Zeile
while (/(.+?)\s+/g) { # mind. 1 beliebiges Zeichen bis Leerzeichen,
# minimal matchen, merken, alle Vorkommen
$c++;
}
}
printf "$c %s\n", $c == 1 ? 'Wort' : 'Woerter';
Erweiterte reguläre Ausdrücke
Siehe http://www.tu-chemnitz.de/docs/perldoc/perlre.html#Extended-Patterns
Reguläre Ausdrücke in split
z.B. Aufsplitten an beliebigen Leerzeichen: @teile = split(/\s+/, $zeile);
Beispiel: Auswertung Apache-Log-Datei
Common Log Format (CLF), in httpd.conf:
LogFormat? "%h %l %u %t \"%r\" %>s %b" common
z.B.
# Klient - user Datum Zeit Request Status Bytes
134.109.200.2 - fri [15/Jan/2008:13:55:36 +0100] "GET /apache_pb.gif HTTP/1.0" 200 2326
Ein möglicher regulärer Ausdruck:
^\S+ - \w+ \[.+?\] "\S+ \S+ \S+" \d+ \d+
while (<>) { # zeilenweise für alle Dateien der Kommandozeile
# Klient user Datum/Zeit Dokument Status Bytes
if (/^(\S+) - (\S+) \[(\S+) .+\] "\S+ (\S+) \S+" (\d+) (\d+)/) {
$bytes += $6; # Bytes summieren
$HOSTS{$1}++; # Hostzugriff merken
} else {
print STDERR "Fehler: $_";
}
}
# Auswertung:
print "Daten: $bytes Bytes\n\n";
# Sortieren: Welcher Klient hat am meisten geholt:
print "Zugriffe : Klient\n";
foreach $h (sort {$HOSTS{$b} <=> $HOSTS{$a}} keys %HOSTS) {
printf "%8d : %s\n", $HOSTS{$h}, $h;
}
Umgang mit Zeichensätzen
- Nationale Sonderzeichen, z.B. Umlaute, sind in verschiedenen Zeichensätzen festgelegt, z.B. ISO-8859-1 = Latin1
- Müssen von Zeichenkettenfunktionen und regulären Ausdrücken beachtet werden.
# Beispiel für Arbeit mit Umlauten im ISO-8859-1-Zeichensatz = Latin1
# seit perl 5.8: Daten sind Latin1, Bildschirmausgabe aber UTF-8-Zeichen
use encoding latin1, STDOUT => "utf8";
# Hier ISO-8859-1 kodiert:
$umlaute = 'Umlaute sind äöüÄÖÜ(ß)';
# z.B. \w umfasst auch Umlaute
($entfernt = $umlaute) =~ s/\w+//g;
print "Normal: $umlaute" .
"\nGross: " . uc($umlaute) .
"\nKlein: " . lc($umlaute) .
"\nEntfernt: .$entfernt.\n";
- Unterstützung für Unicode seit Perl 5.6, ab 5.8 richtig
- Praktisch ist Kodierung UTF-8 interessant.
- Problem: ein Zeichen ist mglw. länger als ein Byte
-> Anpassung für Zeichenkettenfunktionen und reguläre Ausdrücke
#!/usr/bin/perl
# Beispiel für Arbeit mit Umlauten: UTF-8 kodiert
use encoding 'utf8'; # seit Perl 5.8
# use utf8; # nur nötig, wenn z.B. auch Variablennamen UTF-8 einhalten
use open ':utf8'; # zu öffnende Dateien enthalten UTF-8-Daten
# Hier UTF-8 kodiert:
$umlaute = 'Umlaute sind äöüÄÖÜ(ß)';
($entfernt = $umlaute) =~ s/\w+//g; # \w umfasst auch UTF-8-Zeichen
print "Normal: $umlaute" .
"\nGross: " . uc($umläute) .
"\nKlein: " . lc($umläute) .
"\nEntfernt: .$entfernt.\n";
Verwendung von Modulen
- Viele Module für Standard-Aufgaben sind in der Standard-Installation enthalten.
- hierarchisch:
Gruppe::Modul
, z.B. File::Copy
- Listet installierte Module:
perldoc perlmodlib
- Dokumentation zu Modul:
perldoc Modulname
, z.B. perldoc CGI
- Tausende Module via http://www.cpan.org
Verwendung:
-
use Modulname;
- alle Funktionen importieren
-
use Modulname qw(func1 func2);
- nur bestimmte Funktionen
- einmalig externe Datei laden (wie
#include
in C): require 'Dateiname';
Beispiel: Programmargumente mit Optionen verarbeiten:
use Getopt::Std;
# Aufruf: perlskript [-d] [-o datei]
getopts('do:'); # -d ohne Argument, -o mit Argument
$debug = $opt_d; # 1, wenn -d vorhanden
if ($opt_o) { # Wenn -o vorhanden
$output = $opt_o; # verwende Wert
}
...
Mehr: perldoc Getopt::Std; perldoc Getopt::Long
Objekte und Klassen
- Klassen werden als
package
realisiert (in Modulen)
- Objekte werden über Konstruktor erzeugt:
$obj = Class->new();
- So auch Methoden:
$obj->methode();
- Zugriff auf Objekt-Attribute:
$obj->{attr};
Mehr: http://www.tu-chemnitz.de/docs/perldoc/perlobj.html
Beispiel: Kleine Oberfläche mit Modul Tk
use Tk;
my $fenster = MainWindow->new; # Hauptfenster erzeugen
$fenster->geometry('300x150'); # Größe
# Ein Knopf mit Aktion
$fenster->Button(-text => 'Hallo Welt!', -command => \&action)->pack();
# "Warteschleife"
MainLoop;
sub action { print "Hallo Du!\n"; exit; }
Webprogrammierung
CGI-Programme (Common Gateway Interface) für dynamische WWW-Seiten:
- WWW-Browser fordert via HTTP Daten ab
- WWW-Server startet Perl, führt Skript aus und übergibt ggf. Daten
- Skript führt Aktionen aus (z.B. Datenbank-Abfrage) und gibt HTML-Seite aus
- WWW-Server schickt diese an Browser
#!/usr/bin/perl
# erstes CGI-Skript - ohne CGI.pm
print "Content-Type: text/html\n\n";
print <<HTML;
<html><head><title>Mein erstes CGI-Skript</title></head>
<body>
<h1>Hallo Welt!</h1>
</body>
</html>
HTML
- Skript in Webspace kopieren, für TU Chemnitz: Endung
.cgi
- Ausführbar:
chmod +x script.cgi
Modul CGI
definiert HTML-Abkürzungen, Hilfe zur Formularauswertung, Cookies etc.,
siehe perldoc CGI
#!/usr/bin/perl
# CGI-Skript - mit CGI.pm
use CGI qw(:standard);
print header,
start_html('CGI-Skript mit CGI.pm'),
h1('Hallo Welt!'), end_html;
Formulare und deren Auswertung via Funktion param()
#!/usr/bin/perl
# CGI-Skript mit einfachem Formular und Auswertung
use CGI qw(:standard);
use HTML::Entities 'encode_entities'; # Kodierung von HTML-Sonderzeichen
print header,
start_html('Englisches Wörterbuch'),
h1('Englisches Wörterbuch'),
start_form,
'Gesuchtes Muster: ', input({'name'=>'frage'}),
submit,
end_form,
hr;
if (param()) { # Sind Werte übergeben worden? = Formular abgeschickt
my $suchwort = param('frage'); # Zugriff auf formularwert
if ($suchwort) {
print 'Suche nach ', encode_entities($suchwort);
print pre(suche($suchwort)); # schreibe Ergebnisse in <pre>
}
}
print end_html;
sub suche {
my ($wort) = @_;
my $ergebnis = '';
if (! open F, '/usr/share/dict/words') {
return "Kann Datei nicht öffnen: $!\n";
}
while (<F>) {
$ergebnis .= $_ if (/\Q$wort\E/); # Sonderzeichen flüchten
}
close F;
$ergebnis =~ s#$wort#<b>$wort</b>#g; # Suchwort fett markieren: <b>...</b>
return $ergebnis;
}
Arbeit mit SQL-Datenbanken
Schnittstelle zu verschiedenen SQL-Darenbanken via Modul DBI
, siehe perldoc DBI
#!/usr/bin/perl
# Beispiel zum Zugang zu MySQL-Datenbanken
use DBI;
# DB-Verbindung Typ Datenbank Server Login Passwort
$dbh = DBI->connect('DBI:mysql:events:www-db.tu-chemnitz.de', '', '')
or die "Kann Datenbank-Verbindung nicht herstellen: $DBI::errstr\n";
# Abfrage vorbereiten
my $sth = $dbh->prepare('SELECT * FROM event WHERE startdate >= NOW() ORDER BY startdate LIMIT 5');
if ($sth->execute) { # Abfrage starten
# DBI::dump_results($sth); # Zum Test "rohe" Ausgabe
while (my $ref = $sth->fetchrow_arrayref) { # Ergebnisse "abholen"
print " Titel: $$ref[1]\nBeginn: $$ref[4]\n\n";
}
}
$dbh->disconnect(); # DB-Verbindung beenden
Mehr: perldoc DBD::mysql