Next Previous Contents

2. Skripts

In ersten Abschnitt des Tutorials werden werden die Grundlagen der PERL Programmierung behandelt:

2.1 Hello World !

Traditionsbewusst beginnen wir diesen Kurs mit einem Programm, das die Worte "Hello World" auf die Konsole schreibt. Dazu erzeugt man mit seinem Lieblingseditor eine Textdatei (hello.pl), die nur aus folgender Zeile besteht:

 
         print "hello world !\n";
         

Auf der Kommandozeile wird dieses Perlscript mit dem Kommando

           "perl  hello.pl"
         

dem PERL -Runtime zur Ausführung übergeben:

 
          work@netto: > perl hello.pl 
          hello  world !  
          work@netto: > 
         

Der PERL "Inerpreter" verwandelt den PERL Quelltext in der Datei "hello.pl" in Bytecode, der dann mit einer Geschwindigkeit ausgeführt wird, die etwa der Ausführungsgeschwindigkeit von kompiliertem C-Code entspricht. Strenggenommen ist also der PERL-Interpreter kein Interpreter sondern ein "Runtime-Environment". Anders als Shell- oder gängige BASIC Interpreter führt PERL die Syntaxprüfung und Übersetzung in einem einzigen Schritt --nicht komandoweise-- aus. Anders als bei der Java Virtual Machine ist ein direkter Zugriff auf den kompilierten Code über das Dateisystem nicht die Regel, sondern eher die Ausnahme.

Seit PERL Version 5.005 ist das "B-Module" Bestandteil der PERL Distribution, womit der Zugriff auf den Parse Tree und die Ausgabe des Bytecodes relativ einfach möglich geworden sind. ( Das "B" steht für "Backend") Die Optionen des B-Modules umfassen die Ausgabe von C-Programmtext, der mit einem C-Compiler in nativen Code umgewandelt werden kann, die Analyse des Codes auf "gefährliche" Strukturen, die Analyse von Subroutinenaufrufen und Variablengebrauch, sowie die Wiederherstellung von PERL-Code aus Bytecode. Das B-Moduls wird hier allerdings nicht weiter behandelt, da dies den Themenumfang einer Einführung sprengen würde. Die PERL-Distributionen seit der Version 5.6 enthalten eine Einführung in die Arbeit mit diesem Modul, die man mit perldoc perlcompile oder man 1 perlcompile aufrufen werden kann, wenn man sich für dieses Modul interessiert.

PERL-Scripts (genauer : alle interpretierten Un*x Scripts) lassen sich aber auch bequemer ohne den expliziten Aufruf des Runtime-Environments ausführen. Dazu muss in der ersten Zeile des Scriptes hinter einem Kommmentarzeichen ('#' im Unix Jargon auch "she" genannt) und einem Ausrufezeichen ('!' auch "bang" genannt) der volle Pfadname des Runtime angegeben werden. Die komplette Konstruktion "#!/<Pfadname>" wird auch gerne als "shebang" bezeichnet. In diesem, und allen folgenden Beispielen wählen wir den verbreiteten Pfadnamen: "/usr/bin/perl":

          #!/usr/bin/perl
          # 
          # ein Kommentar.......
          #
          print "hello world !\n";
         
Der volle Pfadnamen des PERL-RT-Environments lässt sich mit dem Kommando "which" durch den Aufruf:
          "which perl"
          
bestimmen. Außer der Angabe des Runtime-Environmentss muss auch dafür gesorgt werden dass das Script vom Runtime-Environment gelesen und ausgeführt werden kann. Dazu sind mindestens Lese und Ausführungsrechte für den Benutzer erforderlich.

Da sich auf Un*x Maschinen der aktuelle Pfad aus Sicherheitsgründen meist nicht im Suchpfad befindet wird das Script (-anders als bei DOS Derivaten-) im Allgemeinen nicht automatisch gefunden, wenn es sich im aktuellen Verzeichnis befindet. Zum Aufruf des Scriptes im aktuellen Verzeichnis muss daher noch die Sequenz "./" als Abkürzung für das aktuelle Verzeichnis vorangestellt werden. Mit dem Aufruf

           ./hello.pl
          

lässt dich das Script dann direkt ausführen.

Neben dem shebang

          #!/usr/bin/perl
          

der das Script als PERL-Script ausweist, enthält das Script noch eine Anweisung , die Zeile:

           print "hello world \n";
          

die die Zeichenkette "hello world", gefolgt von einem Zeilenenumbruch ("/n") auf die Konsole schreibt. Anweisungen werden durch ein Semicolon ";" abgeschlossen, das nur bei der letzten Anweisung entfallen kann. Die gängige Anordnung der Anweisungen, bei der jede Anweisung in einer eigenen Zeile steht dient lediglich der Lesbarkeit, und ist nicht zwingend. Das Codefragment :

 
            print hello; print world; print "\n";
          

Geht zwar auch durch den Compiler, fällt aber aus ästhetischen Gründen durch.

Das obige Script zeigt noch ein Element von Perlprogrammen: den Kommentar . Wie in der Unix-Shellprogrammierung werden Kommentare durch ein "she" '#' eingeleitet. Alle auf das '#' folgende Zeichen bis zum Ende der Zeile werden vom Runtime ignoriert, und können beliebige Kommentare enthalten.

Aufgaben...

2.2 Skalare

Der Skalar ist der verbreitetste Datentyp in PERL. Skalare können gewöhnliche alphanumerische Zeichenketten (Strings), sowie Zahlen enthalten.

Zahl oder Zeichenkette

In der Gleichbehandlung von Zahlen und sonstigen Zeichenketten besteht ein Hauptunterschied zwischen PERL und streng typisierten Programmiersprachen wie PASCAL, C, Java, die unterschiedliche Datentypen für alphanumerische Zeichenketten und Zahlenwerte unterschiedlicher Länge und Genauigkeit (int, float, longint,.... ) bereitstellen. Bei der Deklaration einer Variablen muss und kann in PERL also nicht angegeben werden ob es sich um eine Zahl oder eine gewöhnliche Zeichenkette handelt. Das Runtime Environment muss bei jeder Operation aus dem Kontext heraus neu entscheiden worum es sich beim Wert der Variablen handelt. Das Script zahlOderZeichen.pl demonstriert diese Kontextüberprüfung:

         #!/usr/bin/perl -w
         #
         # Zahl oder Zeichen ?
         #    
         $string="eine Zeichenkette";
         $zahl=0.123456;
         #
         print "\n leider kein Ergebnis: ", $string+$string  , "\n" ;
         print "\n Ergebnis: ", $zahl+$zahl  , "\n" ;
         #
         

Hier wird das PERL-Runtime Environment mit dem Schalter -w im warnungsaktivem Modus gestartet, in dem zahlreiche Diagnosemeldungen ausgegeben werden. (Eine Übersicht über die Diagnosemeldungen erhält man durch Eingabe der Kommandos man perldiag oder perldoc perldiag. Es ist sicher keine schlechte Idee sich den Aufruf von PERL mit dem Schalter -w als "persönliche Voreinstellung" anzugewöhnen.

Mit den Anweisungen:

 
         $string="eine Zeichenkette";
         $zahl=0.123456;
         
Werden zwei skalare Variablen mit den Namen string bzw. zahl definiert. Gleichzeitig werden diese Variablen mit dem alphanumerischen Wert "eine Zeichenkette", bzw dem Zahlenwert 0.123456 initialisiert. Mit den beiden folgenden Anweisungen
         print "\n leider kein Ergebnis: ", $string+$string  , "\n" ;
         print "\n Ergebnis: ", $zahl+$zahl  , "\n" ;
         

wird dann versucht das Ergebnis einer Addition der Variable mit sich selbst zu berechnen, und das Ergebnis auf die Konsole auszugeben. Dies führt im ersten Fall zu einem Fehler:

         work@netto:~/perl/progs/very_basic > ./zahlOderZeichen.pl  
         Argument "eine Zeichenkette" isn't numeric in add at ./zahlOderZeichen.pl line 10.
         

im zweiten Fall zum korrekten Ergebnis:

         leider kein Ergebnis: 0
         Ergebnis: 0.246912
         work@netto:~/perl/progs/very_basic > 
         

Der Output von "zahlOderZeichen.pl" demonstriert weitere interessante Eigenschaften von PERL die man je nach Standpunkt als Bug oder Feature einstufen kann:

Beide Features sind interessant für kritische Anwendungen in der Prozessteuerung. Ersteres ist erfreulich, weil falsche Eingaben durch defekte Sensoren oder unvorsichtige Benutzer das Programm nicht zum Absturz bringen, letzteres ist unerfreulich, wenn aufgrund der Existenz eines Rückgabewertes im weiteren Programmverlauf Entscheidungen getroffen werden, die auf falschen Werten basieren.

Zum Umgang mit den eben beschriebenen Problemen bietet PERL ein Konzept das mit den Exceptions moderner objektorienterter Programmiersprachen (Delphi,C++,Java,...) vergleichbar ist. Dazu wird das Schlüsselword eval verwendet. Leider verbietet es sich aus Platz und Zeitgründen an dieser Stelle genauer auf das eval Konzept einzugehen, weitergehende Informationen findet man unter dem Schlüsselwort "eval" in der gängigen PERL-Literatur oder in der online-Hilfe unter "perldoc perlfunc".

Aufgaben...

Definition von skalaren Variablen

Die Definition von skalaren Variablen ist denkbar einfach. Hierzu wird einfach der Variablenname angegeben und mit einem vorangestellen Dollarzeichen "$" wird die Variable als Skalar gekennzeichnet.

Das Script defineScalar.pl zeigt einige Beispiele:

        # !/usr/bin/perl 
        #
        # Variablendefinitionen und nicht-Definitionen
        #
        $var1=0;
        $var2='eine Zeichenkette';
        #
        print "\n erste Variable : \n"; 
        print  $var1;
        print "\n zweite Variable : \n";
        print  $var2;  
        print "\n dritte Variable : \n"; 
        print  $var3
        #
        #   
       

Dies liefert den folgenden Output:

        work@netto:~/perl/progs/very_basic > ./defineScalar.pl 

        erste Variable : 
        0
        zweite Variable : 
        eine Zeichenkette
        dritte Variable : 
        work@netto:~/perl/progs/very_basic >
       

Das PERL Runtime Environment interpretiert das erste Auftreten einer Sequenz "$variablenname" als Deklaration einer skalaren Variablen. Die Tatsache, dass die Variable "$var3" mit der Anweisung

        print $var3  ;
       

angesprochen wird bevor der Variable ein Wert zugewiesen wurde lässt das Runtime Environment dementsprechen unbeindruckt: es wird einfach eine leere Zeichenkette auf die Konsole ausgegeben. Nichtinitialisierte Variablen stören also den Programmablauf nicht, können aber zu unerwünschten Ergebnissen führen. Im warnungsaktiven Modus gibt PERL daher bei jeder uninitialisierten Variable eine Fehlermeldung aus. Noch uninitialisierte Variablen haben den (konstanten) Wert "undef". Zur Abfrage und Manipulation existieren die Funktionen "defined" und "undef". Mit dem Aufruf: defined $myvar wird getestet, ob die Variable myvar definiert wurde, mit dem Aufruf undef $myvar wird der Wert der Variablem $myvar "ent-definiert", soll heißen: auf den Wert "undef" zurückgesetzt.

Aritmetik und Zahlenformate

PERL akzeptiert eine ganze Reihe von Zahlenformaten:

Fließkommazahlen, z.B:

Dezimal :

-0.3 0, 3.1415 ,13

Exponential :

-3.0E-1, 0E0 3.1415E1 1.3E10

Ganzahlen, z.B:

Dezimal :

-1 , 3, 6_5

Oktal :

-01, 03, 0101

Hexadezimal :

-0x1, 0x3, 0x41

Oktalzahlen sind durch eine führende "0", Hexadezimalzahlen durch die Sequenz "0x" gekennzeichnet.

Zur Verbesserung (?) der Lesbarkeit können bei der dezimalen Notation an beliebigen Stellen Unterstriche eingefügt werden, z.B:

 
          10_000_000 , 89_123_123.123_123          
        

PERL Mathematik

Als Erbstück der von PERL verwendeten Standard-C Bibliothek entsprechen die Mathematischen Operatoren in PERL völlig den mathematischen Operatoren in "C" :

Binäre Operatoren

Die folgenden binären Operatoren stehen zur Verfügung um zwei Zahlenwerte miteinander zu verknüpfen:

+ :

Addition (binär)

- :

Subtraktion (binär)

* :

Multiplikation

: / Division

% :

Rest einer Division (Modulo)

**:

Potenzbildung

++:

Inkrement

--:

Dekrement

Unäre Operatoren

Unäre Operatoren sind solche Operatoren, die nur einen Zahlenwert als Argument annehmen. Außerhalb der Informatik spricht man meistens von Funktionen.

+ :

positives Vorzeichen

- :

negatives Vorzeichen

abs($x) :

Absolutbetrag

atan2($x,$y):

arcus tangens (x/y), wobei x ungleich Null sein muss

cos($x) :

Cosinus

exp($x) :

Natürliche Exponentialfunktion

hex :

Konversion aus dem HEX Format

int($x) :

ganzzahliger Anteil einer Fliesskommazahl

log($x) :

Nätürlicher Logaritmus (x>0)

oct :

Konversion aus dem OCT Format

rand :

Wenn rand ohne Argumente aufgerufen wird liefert es einen (gleichverteilten) Zufallswert zwischen 0 und 1 zurueck. wird rand mit einem Argument aufgerufen, dann wird ein gleichverteilter Zufallswert zwischen 0 und dem Argument zurückgeliefert.

sin($x) :

Sinus

sqrt($x) :

Quadratwurzel wobei x gräßer oder gleich Null sein soll

srand :

Initialisiert den Zufallsgenerator neu. Sollte mit einem möglichst zufälligen Argument aufgerufen werden.

Da PERL ursprünglich für und auf Unix-kompatiblen Systemen entwickelt wurde kann PERL auf solchen Systemen auch auf alle Funktionen der Standard C-Library zugreifen (z.B tangens). Dies wird insbesondere C-Programmierer freuen. Diese Funktionen befinden sich im Standard PERL Modul POSIX. Eine Beschreibung aller POSIX-Funktionen erhält man durch die Eingabe von

         perldoc POSIX
       

auf der Konsole.

Zeichenketten und Quotierung

Für viele Programmierer ist die effiziente Umgang mit Zeichenketten das Hauptargument für den Einsatz von PERL. Neben den "Druckbaren Zeichen" die von der Tastatur aus eingegeben werden können unterstützt PERL den ganzen erweiterten ASCII Zeichensatz. Zeichenketten (strings) bestehen aus einer Abfolge von ASCII Zeichen. Zwar lässt sich auch eine Anweisung wie:

         $val = bareword ;               
       

durch den Compiler schmuggeln, wenn die Zeichenkette jedoch nichtdruckbare Sonderzeichen oder Leerstellen enthalten soll, dann muss sie bei der Definition in Anführungszeichen eingeschlossen werden. Hierzu bestehen (wie in der Shellprogrammierung) prinzipiell zwei Möglichkeiten:

Einfache Anführungszeichen (Ticks)

        $val='eine ganze Zeichenkette';
        $val=' ein Tick : \'  '; und ein Backslash : \\';
       

In Ticks eingeschlossene Zeichenketten werden bis auf zwei Ausnahmen ohne weitere Ersetzung verarbeitet. Die Ausgabe von nichtdruckbaren Sonderzeichen oder das Ersetzen von Variablenwerten innerhalb der Zeichenkette ist bei diesem Konstrukt nicht möglich. Die beiden Ausnahmenen sind das Tick, das mit einem Backslash maskiert werden muss, und der Backslash selber (von dem ohne diese Konvention u.U. nicht entschieden werden könnte, ob er nicht ein Tick ausmaskiert.

Bei eigenen ruchlosen Experimenten des Autors dieser Zeilen liess sich der Backslash in solchen nicht doppeldeutigen Situationen aber auch ohne Maskierung unterbringen... )

Ein beliebter Anfängerfehler ist es die falschen Ticks zu verwenden. Neben dem richtigen Tick, das sich auf einer deutschen Tastatur über dem she "#" befindet gibt es auch noch ein falsches Tick (accent). Die Verwendung des falschen Ticks in einem script äussert sich in der Fehlermeldung:

          "Unrecognized character \264 at - line xxx"
         

Innerhalb von doppelten Anführungszeichen werden eine ganze Reihe von Sonderzeichen sowie alle skalaren Variablen ersetzt. Das folgende Script (quotation.pl) demonstriert die Ersetzung des nichtdruckbaren ASCII-Characters Nr 7 (Bell,Klingel) sowie einiger skalarer Variablen:

       #!/usr/bin/perl -w
       #
       # Zeichenersetzung
       #
       # nichtdruckbares ASCII Sonderzeichen (char 007 = BEL)
       # oktal: 
       #
       $val1="ein Piepser:  \07 \n";
       print $val1;
       #
       # hexadezimal
       #
       $val2="ein Piepser:  \x07 \n";
       print $val2;
       #
       # Ersetzung von Variablen durch ihren Wert :
       #
       $v1="Bill";
       $v2=" liebt ";
       $v3=" Melissa ";
       $v4=" nicht! \n";
       print "$v1 $v2 $v3 $v4 ";
 
       

Dies liefert den folgenden Output:

  ein Piepser:   
  ein Piepser:   
  Bill  liebt    Melissa   nicht ! 
 

Wobei (bei geeignet konfigurierter Umgebung) nach dem Ausdruck de Zeichenkette "ein Piepser" noch ein Piepton (ASCII CHR Nr 7) erzeugt wird.

Alle Zeichen des erweiterten ASCII Zeichensatzes lassen sich mithilfe des Backslashes "\" und des ASCII-Zeichencodes erzeugen. Der Zeichencode kann in oktaler Notation, oder nach einem vorangestellten "x" in hexadezimaler Form angegeben werden :

Oktaler Zeichencode

\101 "A" (Character Nr 65)

Hexadezimaler Zeichencode

\x41 ("A" Character Nr 65)

Für einige, besonders häufig gebrauchte Zeichen gibt es auch noch bequemerer Backslashsequenzen, so das man nicht unbedingt deren ASCII-Code wissen muss :

\" :

doppeltes Anführungszeichen

\\ :

backslash \

\n :

neue Zeile (newline)

\r :

Return

\f :

neue Seite (form feed)

\t :

horizontaler Tabulator

\v :

vertikaler Tabulator

\b :

Rückschritt (backspace)

\a :

akustisches Signal

\e :

Escape

\cC :

Control-Zeichen (hier: Ctrl-C)

Schließlich gibt es auch noch Backslash-Sequenzen die die Groß/Kleinschreibung beeinflussen:

\l :

nächster Buchstabe klein

\u :

nächster Buchstabe groß

\L :

(Lowercase) Alle zwischen dem \L und einem schließenden \E eingeschlossenen Buchstaben werden kleingeschrieben.

\U :

(Uppercase) Alle zwischen dem \U und einem schließenden \E eingeschlossenen Buchstaben werden großgeschrieben.

\E :

siehe \L und \U)

Beispiele:

Funktionen für Zeichenketten

Die PERL Onlinehilfe listet unter perldoc perlfunc etwa 30 eingebaute Funktionen auf, die zur Manipulation von Strings verwendet werden können. Die ausgefeilten Möglichkeiten zum Suchen und Ersetzen die Perl bietet werden hier unter dem Stichwort Regular Expressions beschrieben. Im folgenden gehen wir auf eine Auswahl von besonders häufig benutzten Stringfunktionen noch genauer ein:

Das Script "stringFunctions.pl", das diese Funktionen demonstriert, sei hier ohne Kommentar wiedergegeben:

#!/usr/bin/perl
#
#
# Konkatenation
print "\ Konkatenation : \n";
$s1="ersterString";
$s2="zweiterString";
print $s1 . $s2 ;
print "\n\n\n";
#
# chop
#
print "chop :\n";
$s3="012345678";
print chop($s3);
print "\n $s3 \n";
#
# chomp
#
print "chomp :\n";
$s4="012345678 \n \n \n \n";
print chomp($s4);
print "\n $s4 \n";
#
# index
#
print "index :\n";
$s5="012345678";
print index($s5,"45");
print "\n" ;
print index($s5,"52");
print "\n\n" ;
#
# length:
#
print "length : \n";
$s6="0123456789";
print length($s6);
print "\n\n";
#
# lc und uc
#
print "lc und uc \n";
$s7="Gross oder Klein ? \n";
print lc($s7);
print uc($s7);

 

Aufgaben...

2.3 Listen

Eine Liste in PERL ist eine geordnete Sammlung von Skalaren. Die Begriffe "Liste" und "Array" werden im Folgenden weitgehend synonym gebraucht, auch wenn es feinsinnige Unterschiede zwischen "Liste" und "Array" gibt. Prägnant gesagt ist eine Liste eine geordnete Menge von Werten während ein Array eine Variable ist. In der PERL FAQ Nr 4 (Data Manipulation) wird die Frage What is the difference between a list and an array? noch ein wenig intensiver diskutiert. Diese FAQ lässt sich mit dem Kommando perldoc perlfaq4 online aufrufen.

Listen in PERL bieten den vollen Komfort von Arrays (wahlfreier Zugriff über Indices), wie bei den dynamischen Datenstrukturen in PASCAL oder "C" muss aber die Anzahl der Elemente eines solchen "Listenarrays" nicht zum Zeitpunkt des Programmentwurfes feststehen, sondern kann im Rahmen der Beschränkung durch den zur Verfügung stehenden Hauptspeicher beliebig groß werden. Probleme mit der Speicherverwaltung durch "harte Zeiger" wie bei den vorgenannten Programmiersprachen werden in PERL weitgehend vermieden, da sich das Runtime Environment im Hintergrund um die Allokation und insbesondere Disallokation von Speicherplatz kümmert.

Der Klammeraffe "@" spielt für Arrays die gleiche Rolle, die das Dollarzeichen für Skalare spielt: Arrays werden durch vorangestellte Klammeraffen gekenzeichnet. Listen können durch eine simple Aufzählung der Elemente definiert werden:

      @myarray= ("element1","element2","element3","element4");
    
Natürlich können auch Zahlenwerte durch Aufzählung in die Liste aufgenommen werden:
      @myarray= ("element1","element2","element3","element4", -1e23, 3.1415 );
    
Eine komfortable Variante z.B. zum Parsen von Daten ist die Erzeugung eines Arrays mit der "qw" Funktion, die ein Array aus einem String erzeugt indem sie die Grenzen zwischen den Elementen über den Leerraum bestimmt:
      @myarray= qw(" element1 element2  element3 element4  -1e23  3.1415 ");
    
Der Zugriff auf die Elemente der Liste erfolgt einfach über den Index.
    $wert=$myarray[2];
   
für die Abfrage, bzw:
    $myarray[2]=AUSDRUCK;
   
für die Manipulation eines einzelnen Feldes. Ein Zugriff auf ein noch nicht definiertes Feld liefert den Wert "undef", eine Zuweisung an einen Ausdruck vom Typ "$myarray[index]" führt bei einem nicht existierenden Array "myarray" automatisch dazu, das dass Array angelegt wird. Solange sich niemand an der Variablen "$[" zu schaffen gemacht hat (wovon wir im Folgenden immer ausgehen), beginnen die Indices eines Arrays (wie in "C" und JAVA) mit 0. Zu jedem Array "@myarray" existiert eine Variable "$#myarray", die den Index des letzten Elementes enthält.

Das Script "arrayInit.pl" demonstriert das implizite Anlegen eines Arrays, und das anschließende Abfragen des höchsten Index:

 
   #!/usr/bin/perl -w
   #
   # Implizite Definition eines Arrays
   # durch Wertzuweisung
   #
   $myarray[23]="ein Wert";
   $myarray[12]="noch ein Wert";
   print "Höchster Index in \@myarray :   $#myarray";
   

Weitere Funktionen für Listen

Aufgaben...

Kontext

Viele eingebaute Funktionen liefern unterschiedliche Ergebnisse zurück, je nachdem als Ergebnis ein Skalar (sogenannter "skalarer Kontext") oder eine Liste ("Listenkontext") erwartet wird. Das Script:

     
       $scal = localtime ();
       @list = localtime ();

       print "\n\n Ergebnis als Skalar : \n" ;
       print "$scal";
       print "\n\n Ergebnis als Liste  : \n"; 
       print "@list";
     

erzeugt die Ausgabe:

      
      Ergebnis als Skalar : 
      Sun Jun 11 10:24:06 2000

      Ergebnis als Liste  : 
      6 24 10 11 5 100 0 162 1work@netto:~ > 
    

Im skalaren Kontext liefert localtime() das Ergebnis des Systemaufrufes ctime zurück. Im Listenkontext liefert localtime() ein Array, dessen Felder mit mit den Elementen der aktuellen Uhrzeit in der lokalen Zeitzone initialisiert sind:

          
      ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    

Nicht immer wird der Datentyp des gewünschten Ergebnisses aus dem Kontext klar, z.B:

       print localtime();
     

Um in solchen Fällen klare Verhältnisse zu schaffen bietet PERL die Möglichkeit durch Voranstellen der Schlüsselworte scalar oder array den Typ des Rückgabewertes zu beeinflussen. Wenn der Typ des Rückgabewertes nicht explizit oder implizit definiert ist, dann wird ein Skalar zurückgeliefert.

Aufgaben....

2.4 Wahrheitswerte, Schleifen und Iteration

PERL bietet die üblichen Schleifenkonstrukte, also for,while,until Schleifen, sowie (als foreach-Schleife) eine Listeniteration, ähnlich der FOR-Schleife aus der Shellprogrammierung.

for und foreach

Die foreach-Schleife

Die einfachste Schleife is die foreach-Schleife, die einen Anweisungsblock für alle Elemente einer Liste ausführt. Das Aktuelle Listenelement wird über die Variable "$_" angesprochen:

 

    @list = (1,2,3,4,5,6,7,"letztes Element");

   foreach (@list){

    print "$_";
    print "\n";

   }
  

Zum Abschluss dieses Abschnittes darf nicht verschwiegen werden, dass das Schluesselwort foreach eigentlich überflüssig ist: Alle Beispiele in diesem Abschnitt erzeugen funktionsgleichen Code, wenn man das Schlüsselwort "foreach" durch ein schlichtes "for" ersetzt.

Im Folgenden werden wir trotzdem aus Bequemlichkeitsgründen einen gedanklichen Unterschied zwischen "for-Schleife" und und "foreach-Schleife" machen, indem wir mit dem Begriff "for-Schleife" eine Schleife mit Zählvariable, mit dem Begriff "foreach-Schleife" eine Schleife beschreiben, die über die Elemente einer Liste iteriert.

Die for-Schleife

Die for-Schleife ist eine Zählschleife die in Syntax und Funktion weitgehend der for-Schleife in "C" entspricht. Das Script:

   #!/usr/bin/perl -w
   #
   for ($i=0; $i<100; $i++ ){
 
     print "\n $i";  
   }
  

schreibt zeilenweise alle Zahlen von 0 bis 100 auf die Konsole. Die for-Schleife besteht aus den folgenden Komponenten:


  for ( < Definition > ; < Kriterium > ; < Manipulation >  )
  {
    .....
  }

  

Eingeleitet wird die for-Schleife durch das Schlüsselwort for. dann folgen drei durch Semikolons getrennte Ausdrücke innerhalb von eckigen Klammern:

  1. Definition : Ein Ausdruck, der einen Anfangszustand (hier: initialen Wert der Schleifenvariable) festlegt ;
  2. Kriterium : Ein Ausdruck, der eine Bedingung definiert unter der die Schleife fortgesetzt wird.
  3. Manipulation : Ein Ausdruck, vor jedem Schleifendurchlauf ausgeführt wird. (Hier: Inkrementierung der Schleifenvariablen)

Abgeschlossen wird die for-Schliefe durch den in geschweiften Klammer eingeschlossenen Anweisungsblock.

Wie in "C" kann eine for-Schleife mehrere Schleifenvariable bearbeiten. Die Ausdrücke für Definition, Kriterium und Manipulation können jeder für sich aus mehreren unabhängigen Ausdrücken bestehen, die dann durch Kommata ',' getrennt werden. Das Script fibonacci.pl zeigt eine solche Konstruktion:

  #!/usr/bin/perl -w
  #
  for( $new=2, $old=2, $aux=0 ;
        1 ; 
        $aux=$new , $new=2*$old+$new , $old=$aux,  print "$old \n"){}
 

Das Script gibt die Fibonaccizahlen mit der rekursiven Definition f(n+2)= 2*f(n)+f(n+1) und Startwerten f(1)=2,f(2)=2 aus. Diese Zahlen beschreiben das Vermehrungsverhalten von unsterblichen Monsterkaninchen.

while und until

Die while-- und until Schleife in PERL sind besonders einfach strukturiert. Sie bestehen aus dem jeweiligen Schlüsselwort (while oder until), einem in runden Klammern eingeschlossenen Ausdruck der zu einem Wahrheitswert evaluiert werden kann und einem in geschweiften Klammern eingeschlossenen Anweisungsblock:


   $i=0;

   while ( $i <= 10 ) {
    $i++;
    print "$i  \n";
   }

   until ( $i <= 0 ) {
    $i--;
    print "$i  \n";
   }

  

Der Anweisungsblock der while-Schleife wird ausgeführt solange die Testbedingung erfüllt ist, (d.h. Der Test wird vor der eventuellen Ausführung des Blockes durchgeführt), der Anweisungsblock der until-Schleife wird ausgeführt bis die Testbedingung erfüllt ist, (d.h. Der Test wird erst nach der Ausführung des Blockes durgeführt). Im obige Codebeispiel durchläuft $i also die Zahlen von 0 bis 10 vorwärts, und von 10 bis 0 rückwärts.

Wahrheitswerte revisited

Im vorangegangenen Abschnitt hatten wir sehr intuitiv mit dem Begriff des "Wahrheitswertes" gearbeitet. Da PERL keine boolschen Type kennt stellt sich natürlich die Frage, was ein solcher "Wahrheitswert" denn nun wirklich ist. Die Antwort darauf kann erstaunlich kurz ausfallen:

FALSCH ist alles was in einem skalaren Kontext zu zum leeren String ("") oder zu "0" evaluert. Alles andere is "WAHR"

Um es an einigen Beispielen zu illustrieren: Wahr sind:

Falsch sind hingegen:

Dank des "execute" features von PERL lassen sich diese Fälle leicht direkt an der Kommandozeile verifizieren:

   perl -e ' while( 1) {print "forever" } '
   perl -e ' while("nichtleerer String") {print "forever" } '
   perl -e ' while(0) {print "never" } '
   perl -e ' while($undefinierter_SCALAR) {print "never" } '
   perl -e ' while(undef) {print "never" } '
 

Boolsche Ausdrücke (d.h: Ausdrücke die zu Wahrheitswerten evaluieren) lassen sich auch klammern, verneinen, verunden verodern... usw. Dies wird in dem Abschnitt über Abschnitt über Programmverzweigungen noch ausführlicher beschrieben.

Iteration über Listen: "map" und "grep"

Neben der foreach-Schleife gibt es auch noch die Konstrukte map und grep um einen Ausdruck oder einen Anweisungsblock iterativ über Elemente einer Liste auszuführen.

Die Konstrukte:

     grep { ...Block... } @liste;
     grep Expression, @liste
   
evaluieren den Anweisungsblock in geschweiften Klammern bzw. den Ausdruck "Expression" . Falls Block respektive Expression einen wahren Wert zurückliefern, wird das entsprechende Listenelement in die Ergebnisliste eingereiht.

Die "map"-Konstrukte

     map { ...Block... } @liste;
     map  Expression, @liste
   
evaluieren den Block bzw. den Ausdruck für alle Elemente der Liste, und liefern dann eine Liste aller Rückgabewerte zurück. Das Script "mapUndGrep":

     
   #!/usr/bin/perl
   #
   @list=(1,2,3,4,5,6,7);
   #
   print (grep {  eval ( $_ < 5 ) } @list );  print "\n" ;
   print (grep   $_ < 5 , @list );  print "\n" ;
   print (map  {  eval ( $_ + 5 ) } @list );  print "\n" ;
   print (map  $_ + 5  , @list );  print "\n" ;
   
Liefert entsprechend die Ausgabe:
   1234
   1234
   6789101112
   6789101112
  

2.5 Hashtables

Das Wort Hashtable bedeutet eigentlich Wühltisch oder Grabbeltisch .

Hashtables (kurz Hashes) oder assoziative Arrays sind spezielle Arrays, bei denen die Adressierung der Elemente nicht über Indexnummern, sondern über beliebige Zeichenketten erfolgt. Ein bezeichnender Unterschied zwischen PERL und den meisten anderen objektorientierten Programmiersprachen ist die Tatsache, dass Hashtables in PERL eingebaute Datentypen sind, während sie in den meisten anderen OO-Sprachen durch Objekte realisiert werden die von externen Bibliotheken (in JAVA: java.util.Hashtable, in C++: STL o.ä. ) bereitgestellt werden. PERL benutzt Hashtables um Objekte zu realiseren und die C-Konstrukte "struct" und "union" bzw das PASCAL-Konstruct "record" zu ersetzen.

Eine Zeichenkette über die ein Element eines Hashtables angesprochen (indiziert) wird heißt "key" (Schlüssel), der inidzierte Wert selber "value" (Wert). Hashes werden bei der Deklaration durch ein vorangesteltes Prozentzeichen '%' ausgezeichnet.

Arrays mit einer geraden Anzahl von Elementen definieren in natürlicher Weise Hashtables dadurch, dass die Listenelemente mit geradem Index die Keys, und die jeweils unmittelbar darauf folgenden Listenelemente mit ungeradem Index die Values definieren. Auf diese Weise lassen sich Hashes sehr einfach anlegen:

       %billhash=( "vorname" , "Bill", "Nachname" , "Kates", "Firma", "Winzigweich"  );
     

Für Ästheten bietet Perl die Möglichkeit, die Kommata zwischen Key und Value durch den Operator "=>" zu ersetzen:

       %billhash=( "Vorname" => "Bill", "Nachname" => "Kates", "Firma" => "Winzigweich"  );
     

Schließlich gibt es auch noch die Möglichkeit einzelnen Keys Values zuzuweisen, und dem Perl Compiler die Arbeit der Deklaration des Hashes zu überlassen. Dazu wird die Syntax $hashName{<key>}=<value> verwendet:

           $billhash{"Vorname"}  = "Bill";
           $billhash{"Nachname"} = "Kates";
           $billhash{"Firma"}    = "Winzigweich";
     

Das Ergebnis ist in allen Fällen der gleiche Record, der einige Daten von Bill enthält:


         ----------------------------
        |   KEY    |     VALUE       |
        |----------|-----------------| 
        |'Vorname' |  'Bill'         |
        |'Nachname'|  'Kates'        |
        |'Firma'   |  'winzigweich'  |
         ---------------------------- 

       

Um auf einzelne Elemente (Values) des Records zuzugreifen wird das Konstrukt: $<hashName>{<key>}; verwendet:

 
       print "\n", $billHash{"Vorname"},"\n"
       print "\n", $billHash{"Name"},"\n"
       print "\n", $billHash{"Firma"},"\n"
     

Einzelne Felder des Hashes können mit der Funktion delete gelöscht werden.(delete($hashname{key})) :

       delete($billHash{"Vorname"});
       delete($billHash{"Name"});
       delete($billHash{"Firma"});
      

Die Funktion exists : ( exists(%hashname{key})) prüft ob zu einem gegebenen Key ein Value existiert:

      if ( exists($billHash('Vorname'))){
       print $billHash('Vorname');
      }
      

(Wobei die obige if-Anweisung einen dreisten Vorgriff auf das Kapitel über Programmverzweigung darstellt)

Die Funktion keys liefert eine Liste aller Schlüssel: (keys($hashname)):


      foreach keys( %billHash){
       print $billHash($_);
      }
      

Außerdem gibt es noch die Funktion each, die sich durch den Hash durcharbeitet, und bei jedem Aufruf einen zweielemtigen Array, besthend aus key und value zurückliefert. Nachdem der Hash abgearbeitet ist liefert each NULL zurück.

    
     while ( ($key,$value) = each(%billHash)){
       print "\n $key -> $value  \n ";
     }

     

2.6 Erstmal nichts über Typeglobs...

Ausser Skalaren, Listen und Hashtables stellt PERL noch einen etwas ausgefallenen eingebauten Datentyp zur Verfügung, den "Typeglob". Ein Typeglob ist ein Verweis auf alle Objekte eines Namens innerhalb einer "Symboltabelle". Wir werden den Typeglob daher erst zu einem späteren Zeitpunkt zusammen mit den Symboltabellen besprechen.

2.7 Programmverzweigung

Für eine Programmverzweigung nach zwei oder mehreren Alternativen unterstützt PERL ein if/elsif/else Konstrukt, dessen Syntax des if/else Konstruktes unter "C" nachempfunden ist. Das if Konstrukt kann folgende Komponenten enthalten:

if (Bedingung) { ... }

das Schlüsselwort "if" gefolgt von einem Ausdruck der zu einem Wahrheitswert (true,false) evaluiert werden kann, gefolgt von einem Anweisungsblock, der in geschweiften Klammern eingeschlossen ist. (Anders als bei "C" und verwandten Sprachen können die geschweiften Klammmern nicht weggelassen werden !)

elsif (Bedingung) { ... } (optional)

das Schlüsselwort "elsif" gefolgt von einem Ausdruck der zu einem Wahrheitswert (true,false) evaluiert werden kann, gefolgt von einem Anweisungsblock, der in geschweiften Klammern eingeschlossen ist. Ein if/elsif/else Konstrukt kann beliebig viele dieser elsif-Blöcke enthalten. Wenn die nach dem einleitenden "if" formulierte Bedingung nicht erfüllt ist, werden die folgenden elsif Blöcke der Reihe nach untersucht. Sobald die eine Bedingung erüllt ist, verzeigt das Programm in den folgenden Block, eventuell weiter folgende elsif oder else Blöcke werden nicht mehr erreicht. Man beachte auch die eigenwillige Schreibweise: "elsif", nicht elseif Die elsif Konstruktion erlaubt multiple Programmverzweigungen, ähnlich wie die case Konstrukte anderer Sprachen. Ein if/elsif/else Konstrukt kann beliebig viele "elsif" Blöcke enthalten.

else { ... } (optional)

Wenn keine der durch if oder elsif eigeleiteten Bedingungen erfült ist, wird eine der Anweisungsblock hinter dem schlüsselwort else angesprungen.

Das Script "if.pl" demonstriert die Verwendung aller drei Konstrukte:

Mit der Funktion time() wird die Anzahl der seit dem 1.1.1970 verstrichenen Sekunden abgefragt. Anschließend wird geprüft ob, diese Zahl beim Teilen durch 4 einen Rest von 1,2,3 oder keinen Rest ergibt:

     #!/usr/bin/perl
     #
     #
     $now=time();

    if ($now % 4 == 1)

      {
        print "\n\n";
        print "Rest 1 \n";
        print "$now \n\n";
       }

    elsif ( $now % 4 == 2)

      {
        print "\n\n";
        print "Rest 2 \n";
        print "$now \n\n";
       }

    elsif ($now % 4 == 3)

      {
        print "\n\n";
        print "Rest 3 \n";
        print "$now \n\n";
      }

    else
       {
        print "\n\n";
        print "durch 4 teilbare Zahl \n";
        print "$now \n\n";

       }

     

Mehr über Boole´sche Ausdrücke

Logische Ausdrücke können (und sollten) mit runden Klammern "()" geklammert werden. Zur Verknüpfung von logischen Ausdrücken untereinander existieren die Operatoren:

Die letzten vier Operatoren besitzen die gleiche Funktionalität wie die entprechenden Operatoren !,&& und ||, aber eine andere Priorität.

Die folgenden Operatoren können zum Vergleich von Zeichenketten und/oder Zahlen benutzt werden:

Vergleich von Zahlen

Vergleich von Zeichenketten

Außerdem wird auch der Match Operator "=~" häufig benutzt. Damit wird getestet ob eine Zeichenkette ein bestimmtes Muster enthält. Die Verwendung des Matchoperators wird im Abschnitt über reguläre Ausdrücke noch ausführlich besprochen.

Die Verwendung von ungeeigneten Vergleichsoperatoren führt oft zu "unintuitiven" Ergebnissen. Der Codeschnipsel:

 
if ("walter"=="heinz") { print "walter == heinz "; }

liefert die Ausgabe: walter==heinz, weil hier der == Gleichheitsoperator zum Vergleich von Zeichenketten missbraucht wurde. Rechte und linke Seite des "==" Operators werden zum numerischen Wert "0" evaluiert, und dann erst verglichen.

unless und "?:"

Syntaktische Varianten zum if/elsif/else Konstrukt lassen sich mit unless und dem Operator ?: realisieren:

Die Sequenz

unless (expression) { Block }
ist äquivalent zu
if (! (expression)) { Block }
d.h: der Block wird ausgeführt, wenn der Ausdruck (expression) false zurückliefert.

Die "?:" Variante funktioniert wie in C:

 expression ?  Anweisung1 : Anweisung2;

Wenn der Ausdruck expression "true" zurückliefert, wird die Anweisung1 ausgeführt, sonst Anweisung2.

Aufgaben...

2.8 Referenzen

Arrays sind " Listen von Skalaren ". Wie aber realisiert man in PERL Konstrukte wie Matritzen, also zweidimensionale Arrays bzw. Arrays deren Listeneinträge wieder Arrays sind?

Die einfachste Lösung für dieses Problem (und eigentlich für die Erzeugung von komplexen Datenstrukturen generell) ist es sogenannte " Referenzen " zu verwenden. Eine Referenz ist ein Skalar, der auf eine andere Datenstruktur verweist. Mit dem Referenzierungsoperator " \" wird aus einer Datenstruktur eine Referenz erzeugt und zurückgegeben:

  $myscalar="ein scalar";
  @myarray=("ein","array");
  $reference1=\$myscalar;
  $reference2=\@myarray;
  print "\n $reference1 \n";
  print "\n $reference2 \n";
 

Hier werden mit den Zeilen

  $reference1=\$myscalar;
  $reference2=\@myarray;
 

Referenzen auf den Skalar bzw das Array erzeugt. Die Ausgabe des Codesnippets zeigt , das es sich bei den Referenzen nicht um gewöhnliche Skalare oder Arrays handelt:


  SCALAR(0x80eddec) 

  ARRAY(0x80edea0) 
 

Will man anstatt mit den Referenzen wieder mit den ursprünglichen Datenstrukturen arbeiten, dann müssen die Referenzen zuerst dereferenziert werden. Dies geschieht durch das Voranstellen der Zeichen "$","@" oder "%" je nachdem ob die Referenz in einen Skalar, ein Array oder einen Hash umgewandelt werden soll:

  $myscalar="ein scalar";
  @myarray=("ein","array");
  $reference1=\$myscalar;
  $reference2=\@myarray;
  print "\n $$reference1 \n";
  print "\n @$reference2 \n";
 

Erledigt diese Aufgabe korrekt, und liefert die Ausgabe :

  ein scalar 

  ein array 
 

Aber Vorsicht:

Um die Dereferenzierung korrekt zu erledigen muss der Datentyp des zu dereferenzerenden Objektes vorher bekannt sein.

PERL bietet zu diesem Zweck die Funktion "ref", die Auskunft darüber gibt welche Art von Datenstruktur sich hinter der Referenz versteckt:

Rückgabe nch Aufruf von ref ($scalar) :

"" (Leerstring)

Skalar (keine Referenz)

"ARRAY"

Referenz auf Array

"HASH"

Referenz auf Hashtable

"SCALAR"

Referenz auf SCALAR

"CODE"

Referenz auf Funktion

"REF"

Referenz auf Referenz

Die folgenden Scripts demonstrieren noch einmal das Erzeugen und Dereferenzieren von Referenzen auf Verschiedene Datentypen:

Skalare:

  #!/usr/bin/perl
  $scalar="Wert";
  $scalarref=\$scalar;
  print "die Referenz (Adresse)       : ", $scalaref ,"   \n\n";
  print "der (dereferenzierte)   Wert :  $$scalarref  \n\n";
 

Arrays:

 #!/usr/bin/perl
 @array = ( 'AAA', 'BBB', 'CCC' );
 $arrayref = \@array;
 print "der (dereferenzierte)   Wert :   @$arrayref  \n\n";
 

Hashes:


  #!/usr/bin/perl
  %hash = ('1' => 'AAA','2' => 'BBB',  '3' => 'CCC' );
  $hashref = \%hash;
  %derefHash=%$hashref;
  foreach $key (keys(%derefHash))                  # foreach
                 {  print "\n \n";
                    print "$key     -> ";
                    print ($derefHash{"$key"});
                  
                }
   print "\n\n";
 

Anders als Zeiger in "C" enthalten Referenzen keine echten Speicheradressen, die Zuordnung von der Referenz auf die Daten wird im Hintergrund vom PERL-Runtime Environment erledigt. Dieses Verfahren hat den Vorteil, dass die berüchtigten dangling Pointers , also Zeiger, die auf nicht mehr existierende Datenstrukturen verweisen, in PERL nicht existieren. Genausowenig braucht PERL Funktionen zum Allozieren und Freigeben von Speicherplatz. Das PERL Runtime Environment erzeugt die notwendigen Speicherstrukturen einfach bei Bedarf, und gibt den Speicher wieder frei, wenn er nicht mehr benötigt wird (i.e: Wenn keine Referenzen mehr auf das Objekt verweisen).

Wie schon bemerkt lassen sich Referenzen auch verwenden, um kompliziertere Datenstrukturen (Arrays von Arrays, Hashes von Hashes, usw..) zu realisieren. Im Script "matrix.pl" wird ein zweidimensionales Array erzeugt, indem ein Array definiert wird dessen Einträge Referenzen auf weitere Arrays sind:

 
 #!/usr/bin/perl
 #
 #
 @line1=(11,12,13);
 @line2=(21,22,23);   
 @line3=(31,32,33); 
 @matrix=(\@line1,\@line2,\@line3);
 for($i=0;$i<=2;$i++){
   $ref=$matrix[$i];
   @ar=@$ref;
   print @ar,"\n";
 }
 

Eine sparsame syntaktische Variante beim Erzeugen von Referenzen auf Arrays ist die Auflistung der Elemente in eckigen Klammern:

 $ref=["Element 1","Element 2","Element 3"];
 

Dies wirkt ähnlich wie der schon bekannte Ausdruck

   @array=("Element 1","Element 2","Element 3");
 

Während die zweite Variante das neu erzeugte Array zurückliefert, gibt die erste Variante eine Referenz auf das neu erzeugte Array zurück.

2.9 Dateien und Pipes

Filehandles

Zur Arbeit mit Ein und Ausgabeströmen bietet PERL den Datentyp "Filehandle". Filehandles bieten eine einheitliche Schnittstelle für die Kommunikation mit Dateien und Prozessen.

Um eine Datei im aktuellen Verzeichnis zum Lesen zu öffnen legt man mit der Anweisung

 
 open (FILEHANDLE,"< Dateiname") ;
 
ein Filehandle an, über das von jetzt an lesend auf die Datei zugegriffen werden kann. Filehandles für den schreibenden Zugriff auf Dateien erzeugt man mit den Umlenkungszeichen ">" und ">>:";

 open (FILEHANDLE,"> Dateiname"); 
 open (FILEHANDLE,">> Dateiname");  
 

Der Unterschied zwischen beiden Zugrifsmethoden > und >> liegt in der Art, wie beim Schreiben in die Datei mit den bereits vorhandenen Daten umgangen wird. Bei der ersten Variante (">") wird die Datei mit den neuen Daten überschrieben ("overwrite"), und die alten Daten gehen verloren, bei der zweiten Variante (">>") werden die neuen Daten an das Ende der Datei angehängt ("append"), die alten Daten bleiben erhalten.

Wie in anderen Programmiersprachen wird der Lese/Schreibzugriff in der Regel gepuffert. (D.h: es werden bei jedem Lese/Schreibzugriff gleich eine größere Menge Daten als angefordert von der Platte gelesen, und im Speicher für weitere Operationen zur Verfügung gehalten). Ähnlich wird beim Schreiben vorgegangen: Die zu schreibenden Zeichen werden zuerst in einen Zwischenspeicher, und erst dann, in grösseren Portionen, auf die Platte geschrieben.

Durch die close-Anweisung wird der Zwischenspeicher engültig geleert,und die belegten Ressourcen wieder freigegeben. Obwohl dies eigentlich am Ende der Programmausführung durch das Runtime Environment automatisch erfolgen sollte ist es (wie in anderen Programmiersprachen auch) guter Stil, offene Filehandles möglichst früh zu schließen. Zum einen erfolgt keine Speicherbelegung durch ungenutzte Filehandles, zum anderen kann man sicher sein, dass bei einem anschließenden unsanften Programmabbruch (Steckerziehen...) tatsächlich alle Daten geschrieben wurden. Die Syntax der close-Anweisung ist ein einfaches

   close <FILEHANDLE>;
  

Für den lesenden Zugriff auf Dateien spielt die Pufferung keine so bedeutende Rolle. Man kann Dateien öffnen, aus Dateien lesen und dann ohne vorheriges Schließen mit dem gerade aktiven Filehandle eine andere Datei öffnen. In diesem Fall wird der Dateizeiger des Filehandles aber nicht zurückgesetzt, sodass die neu geöffnete Datei i.A. nicht von vorne nach hinten gelesen wird. Dieser Effekt wird i.A nicht gewünscht sein.

In der Regel wird der weitere Programmverlauf davon abhängen, ob das Anlegen eines Dateihandles erfolgreich verlaufen ist. "open" liefert im Erfolgsfall ein "TRUE", bei Miserfolg ein "FALSE" zurück. In Skripts findet man "open" oft in Kombination mit der "die" Funktion, die das Script mit einer Fehlermeldung beendet. Der folgende Codeschnipsel versucht die Datei "datei.txt" zu öffnen, und beendet die weitere Programmausführung mit der Ausgabe einer Fehlermeldung und des aktuellen SystemErrorString "$!", falls das Öffnen fehlschlägt:

  open(DBASE, '< datei.txt')             #  "datei.txt" oeffnen
        || die "datei.txt kann nicht geoeffnet werden : $!";
  
Der gute Stil erfordert, dass jeder Aufruf von "open" von entsprechendem Code zum Abfangen der Fehler begleitet wird. Wenn wir im Folgenden gelegentlich davon abweichen, dann geschieht das aus didaktischen Gründen, um den Umfang der Beispiele möglichst gering zu halten...nicht etwa aus Faulheit...aehem..ja.

Für den lesenden Zugriff stellt PERL den "<>"-Operator bereit. Dieser arbeitet recht unterschiedlich, je nachdem ob er im skalaren oder im Listenkontext aufgerufen wird. Im Listenkontext liefert der Ausdruck <FILEHANDLE> ein Array, in dessen Feldern sich die Zeilen der Eingabedatei befinden (readfileArray.pl):

 #!/usr/bin/perl
 #
 # die Passworddatei als Array auslesen
 # (<...> Operator im Listenkontext)
 #
 open (INFILE,"/etc/passwd");  
 @ar= (<INFILE>);
 print join ("/n",@ar);
 

Im skalaren Kontext liefert jeder Aufruf von <FILEHANDLE> die aktuelle Zeile der Datei und setzt (soweit möglich)die aktuelle Zeile eine Zeile weiter. Im folgenden Beispiel readfileScalar wird bei jedem Aufruf von <INFILE> eine neue Zeile der Passworddatei zurückgeliefert, solange bis keine Zeile mehr zurückgeliefert werden kann (undef).

 #!/usr/bin/perl
 #
 # einen unendlichen Strom von Zeichen  
 # Zeilenweise einlesen
 #
 open (INFILE,"/etc/passwd");  

   while (<INFILE>){
     $input=$_;
     print("-> $input ");
   }

 

Vordefinierte Filehandles

Für die Unix-Kanäle stdin (Standardeingabe), stdout (Standardausgabe) und stderr (Standard-Fehlerausgabe) existieren in PERL die vordefinierten Filehandles STDIN , STDOUT , STDERR Der Aufruf des <> Operators ohne eingeschlossenes Filehandle liefert (wie der Ausdruck <STDIN>) die aktuelle Eingabezeile.

Pipes

Im Un*x Umfeld sind Pipelines (kurz Pipes ) ein beliebtes Werkzeug der Shellprogrammierung um die Eingabe und Ausgabe verschiedener Programme miteinander zu verknüpfen. So kann man zum Beispiel die Ausgabe des Kommandos "find /etc", das eine Liste aller Dateien unterhalb des Verzeichnisses "/etc" erzeugt, mit der Eingabe des Kommandos "grep pass" verbinden, das einen Eingabestrom nach Zeilen durchsucht die die Zeichenkette "pass" enthalten:

  find /etc | grep pass
 
Zur Umleitung dient das sogenannte "Pipezeichen" |, das Ergebnis ist eine Liste aller Dateien, die die Zeichenkette "pass" enthalten.

Unter Betriebssystemen die einen brauchbaren Pipe-Mechanismus bereitstellen lässt sich die Interprozesskommunikation über pipes auch direkt aus Perl Programmen heraus verwenden. Dabei wird ein aus dem PERL-Programm heraus ein Prozess gestartet, in dessen Eingabekanal dass PERL Script Daten hineinschreibt, oder aus dessen Ausgabekanal das PERL Script Daten herausliest. Schreiben und Lesen erfolgen jeweils über ein Filehandle.

Um aus eine Pipe zu lesen wird ein Filehandle erzeugt, bei dem anstatt einem Dateinamen der Aufruf des Prozesses, gefolgt von einem Pipesign angegeben wird:

  open ( FILEHANDLE ,"programmaufruf |")
 

Um in eine Pipe zu schreiben wird ein Filehandle erzeugt bei dem anstatt einem Dateinamen ein Pipesign gefolgt vom Aufruf des Prozesses angegeben wird:

  open ( FILEHANDLE ,"| programmaufruf ")
 

Der folgende Code (searchPass.pl) tut zwar nichts weiter als die oben vorgestellte Shell-Pipe

 find /etc | grep pass
 

Demonstriert aber das Öffnen und Schließen von Pipes mit PERL:

 #!/usr/bin/perl
 open (INPUT, "find /etc |");
 @input=<INPUT>;
 close(INPUT);
 open (OUTPUT, "| grep pass ");
 print OUTPUT @input ;
 close(OUTPUT);
 


Next Previous Contents