Programmieren in PERL

Referenz

von Roland Bernert, R egionaler Arbeitskreis Internet am Oberschulamt Karlsruhe


Inhalt
 
 

Start: #! /usr/bin/perl (bei Linux) oder #! /usr/local/bin/perl (bei UNI-UNIX)

 
chop entfernt das letzte Zeichen einer Variablen Bsp: chop($name); 
$x=chop($x); #weist $x das letzte Zeichen von $x zu
print gibt Werte und Text auf die Standardausgabe aus Bsp: print "Dies ist ein Texst von $name\n" 
print FILEHANDLE "Test\n"; gibt Text in die Datei
Konstanten: Zahlen $zahl=12; $zahl=-12 usw. HexZahlen $hexzahl=0xF1 oder $hexstring="0xF1"; 
$hexzahl=hex($hexstring); 
Strings $text="Dies ist ein Test von $name"; $name wird automatisch ersetzt. 
$text='Dies ist ein Test von $name' $name nicht ersetzt 
$text='Dies ist \" ein Test\"'; ein Text mit Hochkomma 
Bei doppelten Anführungszeichen kann man Steuerzeichen mit  
einem vorgestellten Backslash eingeben.
 
 
Die Steuerzeichen sind:
\n Newline 
\r Return 
\t Tabulator 
\f FormFeed 
\b Backspace 
\v Vertikaltabulator 
\a Tonsignal 
\e ESC 
\007 Oktaler Wert (007 = Ton) 
\x7F hexadez. ASCII-Wert 
\cC jedes Controllzeichen (CTRL-C) 
\\ einen Backslash 
\" ein Hochkomma 
\l nächsten Buchstaben klein 
\L alle folgenden Buchstabe klein (bis \E) 
\u alle Buchstaben groß 
\U alle folgenden Buchstaben groß (bis \E) 
\E \L oder \U beenden 
Kontrollstrukturen:

if
Bsp: if ($name eq "roland") {
-- Anweisungen;
} else {
-- Anweisungen;
}
Es geht auch: Anweisung if (Bedingung); oder
> Anweisung && Bedingung;
Es gibt noch eine dritte Möglichkeit:
Bed1 ? Anweis1 : Anweis2; falls Bed1 dann Anweis1 sonst Anweis2;
Das Gegenteil von if ist unless Gegenteil von while ist until: For-Anweisung:
for ($i=1; $i<=10; $i++) { ..... }

Vorzeitiges Verlassen von Scheifen:
Der Operator LAST: while ( ... ) {
......
last; hier wird aus der Schleife gesprungen
}

Der Operator NEXT: Schleife nochmals durchlaufen
while ( ... ) {
....
....
if (...) {
next; nochmals; aber vor das Ende springen
}
}

 Der Operator REDO: Schleife nochmals von vorne durchlaufen
while (....) {
.....
if ( ... ) {
redo; nochmals; aber an den Anfang der Schleife
}
}
 
Listen: 
 
@liste = ("Item1","Item2","Item3");
Arrays: print $liste[2]; Zählung beginnt bei Null  Bsp.: $i=1; print $liste[$i]; 
$anzahl=@liste ; liefert die Anzahl der Listenelemente
Assoziative Arrays %Arrayname=("Roland","WertvonRoland","Helga","WertvonHelga"); 
$name="Roland"; 
print $Arrayname{$name}; 
delete $Arrayname{"Roland"}; löscht das Wertepaar Roland 
Match-Operator: if ($name=~ /^roland/) { ..... } Zeichenkette soll mit roland beginnen
Matchoperator
Ignore Case: if ($name =~ /^roland/i) { .... } (Groß-Kleinschreibung ignorieren)
Wort-Begrenzung: WORD-BOUNDARY
if ($name= /^roland\b/i) { .... } nur roland am Anfang suchen

Text ersetzen: $name=~ s/\W.*//;
\W: alles außer einem Buchstaben, einer Zahl oder einem Unterstrich;
also alles, was nicht zu einem Wort gehört.
.*: Jedes Zeichen von dort bis zum Ende dieser Zeile
//: durch nichts ersetzen
Beispiele: $name=~ s/,.*// # alles hinter Komma vergessen
$name=~ s/\W.*// # alles hinter erstem Wort vergessen
$name=~ /^Roland/ # wenn die ersten Zeichen Roland
$name=~ /^Roland\b/i # wenn das erste Wort Roland, egal ob Groß-oder Kleinbuchstaben

Translate-Operator:
$name=~ tr/A-Z/a-z/; alles in Kleinbuchstaben umwandeln
oder
tr /a/z/; ersetzt in $_ alle a durch z
tr /a-z/A-Z/; usw. # alles in Großbuchstaben verwandeln
tr gibt die Zahl der Ersetzungen zurück: z.B.: $count=tr/a/z/; 


Unterprogamme:
sub procedurename {
local {$wert1, $wert2 ...)=@_; Übergabearray
.....
}

Aufruf: &procedurename($name1, $wert1,...);

Logische Operatoren:
! : not; Bsp: if (! $name eq "roland") { ... }
|| : oder: Bsp: if ((....) || (....)) { .... }
&&: und: BSP: if ((...] && (...)) { Anweisungen }
Filehandles: Standareingabe: $a=<STDIN>;
chop($a = <STDIN>); gleich Newline abschneiden
while (<>) { .. } holt sich den Filenamen von der Eingabezeile; die Dateinamen kommen dabei in @ARGV
also könnte man auch folgendes machen:
@ARGV=("Datei1","Datei2","Datei3);
while (<>) { ... };
open(Filehandlename,"dateiname");
while($name=<Filehandlename>) {
chop($name);
print "$name\n";
}

Ein ganzes Array holen: @zeilen=<FILEHANLDE>;

ein vorgesetztes -M gibt die Zeit in Tagen seit der letzten Speicherung
Bsp: if (-M Filehandlename >7) { .... } schon eine Woche her

man kann auch automatisch eine Mail abschicken
open(MAIL,"|mail za186@lehrer.uni-karlsruhe.de");
print MAIL "Dies ist ein Test\n";
close(MAIL);

Bsp: Eine komplette Datei ausgeben
open(Filehandle,"/etc/passwd");
while (<Filehandle>) {
chop;
print "$_\n";
}

Bsp: Eine Datei kopieren
open(INFILE, "dateiname");
open(OUTFile,"outdateiname");
while (<INFILE>) {
print OUTFILE $_;
}
close(OUTFILE);
close(INFILE);

Bsp: Alle Dateien holen
while ($nextname = </etc/host*.*>) {
print "$nextname\n";
oder while ($nextname = </etc/host*>) {

$nextname = s#.*/##;
print "$nextname\n";
} alles von dem letzten / entfernen !

Öffnen für Ausgabe:
open(AusgabeName,">dateiname");
print AusgabeName "Dies ist ein test\n";

Wildcards:
while ($filename=<*.*>) {
print "ich habe $filename gefunden\n";
oder: @files=</etc/*.htm>; liefert alle *.htm-Dateien

Ein ganzes Directory lesen:
opendir(ETCDIR, "/etc");
while ($name=readdir(ETCDIR)) { print "$name\n"; usw........}
closedir(ETCDIR);

Datei umbenennen: rename($filename,"$filename.neu");

Datei löschen: unlink ("dateiname");
unlink (<*.html>); wie rm *.html

Datei auf Existens testen: if (-e $filename) { ... $filename existiert };

Datei ist lesbar: if (-r $filename) { ... $filename ist lesbar };

Datei ist beschreibbar: if (-w $filename) { ... $filename ist schreibbar };
Hier eine Liste der Datei-Test-Zeichen
-r lesbar 
-w schreibbar 
-x ausführbar 
-o gehört dem Benutzer 
-e existiert 
-z existiert und hat die Größe Null 
-s existiert und hat eine Größe 
-f ist eine normale Datei 
-d ist ein Directory 
-l ist ein symb. Link 
-S ist ein Socket 
-p ist ein FIFO 
-b ist ein Festplatte 
-c ist ein Zeichengerät 
-t ist ein Ausgabegerät 
-T enthält Text 
-B enthält Binärdaten 
-M Zeit seit der letzten Änderung in Tagen 
-A Zeit seit dem letzten Zugriff 
es gibt noch weitere
Anlegen eines Verzeichnisses: mkdir ("VerzName", 0777);

Löschen eines Verzeichnisses: rmdir ("verzname");

Zugriffsrechte ändern: chmod(0666,"dateiname");

Array auf Datenbank abbilden:
dbmopen(%AssozArrayName,"dbname",0666);
$AssozArrayName{$name} = .....
dbmclose(%AssozArrayName);



 
Der Operator KEYS @liste=keys(%AssozArrayName); ergibt eine Liste aller Schlüssel des assoziativen Arrays 
man kann auch sortieren lassen: @liste=(sort keys(%AssozArrayName));
Der Operator VALUES:  liefert wie KEYS statt die Schlüssel die Liste der Werte im assosiativen Array 
Bsp: @liste=values(%AssozArrayName);
Der Operator EACH:  liefert Schlüssel und Werte eines assoziativen Arrays 
Bsp: while (($key,$wert)=each(%AssozArrayName)) { 
print "Der Schlüssel $key hat den Wert $wert\n"; 
}
Der Operator FOREACH foreach $name (sort keys(%AssozArrayName)) { 
print "$name\n";} druckt alle Schlüssel sortiert aus. 
foreach $i (@eineListe) { ..... } 
man kann die Variable $i auch weglassen, Perl nimmt dann als Standardvariable $_ 
Bsp: foreach (@eineListe) { print; } 
Bsp: foreach $b (reverse @a) { print $b }; alles verkehrt herum


 
Operatoren für Zeichenketten:
hintereinanderketten Bsp: $name="Hallo" . " Roland";
Vergleichsoperatoren:
Zahlen Strings
gleich  ==  eq
ungleich !=  ne
kleiner  lt
größer  gt
kleiner/gleich  <=  le
größer/gleich >=  ge
Wiederholung von Zeichenketten: 
$name="Roland" x 3 # dreimal Roland 

Der Operator SPLIT: teilt einen String mit Trennzeichen in Einzelteile auf
$name="Roland:Bernert";
@Felder=split(/:/,$name);
print "$Felder[0] und $Felder[1]\n";
oder
($vorname,$zuname)=split(/:/,$name);
print "$vorname und $zuname\n";
mehrere Leerzeichen als eines auffassen: @woerter=split(/\s+/,$_);
oder mehrere Doppelpunkte: @woerter=split(/:+/,$zweile);

Der Operator JOIN: setzt Strings wieder zusammen
$ausgabe=join(":",@Felder);

Teil einer Zeichenkette:
$Position=index($String,$substring); wie Pascal Pos(sub,String)
$Position=rindex($String,$Substring); von rechts kommend
$teilstring=substr($string,$pos,$anzahl); wie Pascal copy
man kann aber auch von hinten abschneiden:
$teilstring=substr($string,-4,4); die letzten 4 Zeichen

man kann aber auch ersetzen: substr($string,1,5)="Test"; 


Sortieren von Strings: @sortliste = sort @eineListe sortiert Zeichenketten

Jetzt soll nummerisch sortiert werden:
sub by_number { Unterprogramm
if ($a<$b) { -1; } elsif ($a==$b) {0;} elsif ($a>$b) {1;} }
@sortnumlist = sort &by_number @eineNumliste;

Das Unterprogramm geht noch einfacher:
sub by_number { $a<=> $b; } oder noch einfacher zum Sortieren:
@sortnumlist = sort { $a <=> $b } @eineNumListe;

Man kann auch zuerst numerisch und - falls gleich - dann als Zeichenkette sortieren
sub by_erst_numerisch { ($a <=> $b) || ($a cmp $B); } 


Reguläre Ausdrücke:
if (/abc/) { print "$_"; } falls in $_ die Zeichenkette abc gefunden wird, dann gib sie aus.
s/abc/xyz/; # ersetze in $_ die erste Zeichenkette abc durch xyz
s/abc/xyz/g; # ersetze in $_ alle Zeichenketten abc durch xyz
Bsp: s/a-z/A-Z/;

soll nicht nur die erste auftretende Zeichenkette abc erstezt werden, so hängt man den Operator g an:
Bsp: s/abc/xyz/g;

Es geht auch mit Variablen: Bsp: s/abc/$name/;

Weitere Möglichkeiten:
if (/abc/) { print "$_"; } wie grep abc $_
if (/ab*c/) { .. } keine oder mehrere b's zwischen a und c
s/ab*c/def/ ersetzt ab....bc durch def
* = kein oder mehr
+ = ein oder mehr
? = kein oder einmal
Bsp: $_="Roland xxxxxxxxxxxxx ist hier"
s/x*/Bernert/; liefert Roland Bernert ist hier

/x{5,10}/ x fünf- bis 10 mal hintereinander
/x{5, }/ mindestens 5mal ein x
/x{0,5}/ höchstens 5mal ein x
/a.*c/ alle Zeichen an a ab bis zum c aber kein Newline
/a(.)b\1/; ein a, ein bel. Zeichen, ein b und dasselbe Zeichen.

(die 1 ist ein Platzhalter für die erste Klammer)
/a(.)b(.)\2\1/ ein a, ein bel. erstes Zeichen, ein b, ein bel.
zweites Zeichen, nochmals das zweite Zeichen, dann nochmals das erste Zeichen
/a(.*)b\1/ passt auf ein a, bel. Zeichenfolge bis b, dann nochmals dieselbe Zeichenfolge
/a|b|c/ entweder a oder b oder c
/a./ paßt zu einem Wort das mit a beginnt aber nicht mit Newline endet
/[aeiouAEOIU]/ paßt zu allen Vokalen
/[0-9]/ paßt zu allen Ziffern
/[^0-9]/ keine Ziffer - Verneinung

Hierzu gibt es Vereinfachungen:
\d = [0-9] /D = [^0-9]
\w = [a-zA_Z0-9_] \W = [^a-zA_Z0-9_]
\s = [ \r\t\n\f] \S =[^ \r\t\n\f] 


Zeichenmuster verankern:
/Roland\b/ Übereinstimmung mit dem Wort Roland
/\hallo/ Übereinstimmung mit allo oder alloa, aber nicht mit hallo
/\ball\b/ Übereinstimmung nur mit all, aber nicht hallo und nicht Ball
/\bRoland\B/ Übereinstimmung mit Rolandistda aber nicht mit Roland Bernert
^ verankert ein Zeichenmuster am Beginn eines Strings
$ verankert ein Zeichenmuster am Ende eines Strings (z.B c$)
abc* Übereinstimmung mit ab oder abc oder abccccc usw.
(abc)* Übereinstimmung mit "" oder abc oder abcabcabc usw.
a|b|cd Übereinstimmung mit a oder b oder cd

$name= /(.)\1/; zwei gleiche Zeichen
if (<STDIN> = /^jJ/) { .... } es wurde am Anfang j oder J eingegeben
if (<STDIN>= /^j\i/) { ... } das gleiche: i = Ignore Case !

man kann auch ein anderes Trennzeichen vereinbaren, z.B.
m@^j\i@ oder m#^j\i# @ oder # als Trennzeichen 


Variablenersetzung benutzen:
$wort="Vogel";
$satz="Ein Vogel kann fliegen";
if ($satz= /\b$wort\b/) { ......}; das Wort kommt als freist. Wort im Satz vor !
if ($satz= /\Q$wort\E/) { ....}; das Wort kommt irgendwie im Satz vor ! 
Spezielle Variablen:
Nach einem Mustervergleich werden die Variablen $1, $2, $3 usw. auf die Werte \1, \2, \3 usw. gesetzt.
Bsp: /(a.)/ liefert $1 ="a" + ein beliebiges Zeichen
/(\w+)\W+(\w+)/; liefert $1 = erstes Wort, $2 = zweites Wort
s/(\w+)/#$1#/g; jedes Wort wird durch #wort# ersetzt.
$& = Teil der Zeichenkette, der tatsächlich übereinstimmt
$`= Teil der Zeichenkette vor dem übereinstimmenden Ausdruck
$' = Teile der Zeichenkette hinter dem übereinstimmenden Ausdruck 
Arbeiten mit SYSTEM() und EXEC():
system("date"); führt das UNIX-Kommande date aus
system("date >dateiname"); Ausgabe in die Datei dateiname
system("grep test dateiname >neuedatei"); wie unter Unix
besser ist jedoch:
system "grep", "test","dateiname",">neudateiname";

man kann auch schreiben: $nun_ist_es="Zeit" . `date`; Backqoutes benutzen 


Es exisitiert ein assoziatives Array %ENV, das die Umgebungsvariablen enthält !
z.B. print $ENV{"PATH"}
exec "date"; verläßt Perl und startet date, Perl gibt es dann nicht mehr !
exit; Perl sofort beenden 
Einen Prozess starten und nicht warten wird erlaubt durch ein FILEHANDLE:
Bsp: open(LPR,"|lpr -P ascii Testdatei"); im Hintergrund etwas ausdrucken
print LPR @irgendeinArray;
close(LPR);

oder zum Lesen
open(EINGABEFILE, "ls -a|");
print "$_\n"; es wird das Process ls -a gestartet 


Informationen über Benutzer:
getpwnam("roland"); liefert alles des Benutzers Roland
getpwuid(123); liefert alles über den Benutzer mit der ID 123

Die zurückgegebenen Felder sind
($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell)
Bsp: $roland_home=(getpwname(""Roland"))[7]; liefert das Homeverzeichnis

Bsp: Alle Einträge holen:
setpwent; Initialisieren
while (@liste = getpwent) { nächste Zeile holen
($login, $home)=@liste[0,7]; loginName und HomeVerzeichnis
print .......
}
endpwent;

und dann noch nach Home-Verzeichnis sortiert ausgeben
@keys = sort { $home{$a} cmp $home{$b} } keys %home;
foreach $login (@keys) {
print ".......\n";


Packen und entpacken von binären Daten:
$a = pack("CCCC", 10, 20, 123 231); C steht für ein einzelnes Zeichen
Jede zahl wird in ein Zeichen konvertiert also eine zeichenkette aus 4 Zeichen.
$a = pack("C4",10,20,123,231); liefert das gleiche
$a = pack("C*",10,20, ....) alle Zeichen bis zum Ende
$a=pack("l", 0x41424344); erzeugt einen Longwert (l) aus abcd
($wert1,$wert2) = unpack("ll","ABCDEFGH"); erzeugt $wert1=ABCD und wert2=EFGH
@liste = unpack("C*","Roland"); liefert eine Liste von ASCII-Zeichen

Bsp: Die Hostadresse holen:
($addr) =(gethostbyname("uni-karlsruhe.de"))[4];
print "Adresse:" . join(".", unpack("C4", $addr)), "\n"; 


Benutzerdatenbanken: DBM-Datenbanken und DBM-Arrays:
Hinweise: Durch Öffnen einer DBM-Datenbank wird ein assoziatives Array angelegt und mit
der Datenbank verknüpft. Über diese ass. Array kann auf die Datenbank zugegriffen
und geändert werden. Die Änderungen geschehen synchron, d.h., daß man sich nicht
weiter darum kümmern muß .

Öffnen: dbmopen(%Arraname, "dbmfilename", $mode);
$mode definiert die Zugriffsrechte: z.B. $mode=0644;
Schließen: dbmclose(%Arrayname);

Nach dem Öffnen kann man alles machen, was mit assoziativen Array möglich ist.
Die Änderungen werden sofort in der Datei aktualsiert. 


Ein kleines Perlprogramm zum Schluß:
es liefert eine HTML-Seite mit, bei der etwas eingegeben wurde in der Form
...../za186/cgi-bin/perl5.cgi?Suchstring=test

#! /usr/local/bin/perl
print ("Content-Type: Text/html\n\n");
print "<HTML><HEAD><TITLE> </TITLE></HEAD>";
print "<BODY>";
if ($ENV{"QUERY_STRING"}) {

}
print "</BODY> </HTML> "; 
und noch ein PERL-Programm
#! /usr/bin/perl
# testet die Anwendung des Textersetzen-Operators s
print "Content-type: Text/html\n\n";
print "<HTML><HEAD><TITLE></TITLE></HEAD>\n";
print "<BODY>\n";
print "Test für s-Operator<BR>\n";
if ($ENV{"QUERY_STRING"}) { }
print "</BODY></HTML>\n"; 
10.07.1997 Roland Bernert