Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Konvertieren, int ceil sprintf hex oct floor Runden AbschneidenPerl Kurs Skripting Datei Ein/Ausgabe Verzeichnis open read write STDOUT STDIN print sysopen sysread syswrite Datei Ein/Ausgabe Verzeichnis open read write STDOUT STDIN print sysopen sysread syswrite Perl Kurs Kapitel 13

13.1. Datei Ein/Ausgabe           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Was bringt einem die beste Programmiersprache, wenn man keine Daten Speichern oder wieder Laden kann. Bevor Sie nun aus einer Datei lesen oder schreiben, müssen Sie diese erst mal  öffnen. Hierzu verwenden man das in C schon bekannte Schlüsselwort open. Hier ist der Syntax von open ...

open(HANDLE,  "modus $DATEINAME");

Hiermit öffnen Sie eine Datei Namens $DATEINAME mit dem Modus modus. Mit dem erzeugten Datei-HANDLE können Sie anschließend die Datei weiterverarbeiten. Folgende Modis stehen Ihnen für das öffnen einer Datei zur Verfügung ...

Sollte das öffnen einer Datei fehlschlagen, müssen Sie nicht wie in anderen Programmiersprachen den Rückgabewert der Funktion überprüfen sondern lediglich eine or-Verknüpfung mit der Funktion 'die' machen ...

open(HANDLE, "< $file")
         or
         die "\nKonnte Datei $file nicht zum Lesen öffnen\n";

Mit dem folgendem Skript können Sie aus einer Datei lesen ...

#!/usr/bin/perl -w

print "Welche Datei wollen Sie zum Lesen öffenen : " ;
chomp ( $file=<>);

open(FH, "< $file")
 or
 die "\nKonnte die Datei $file nicht zum Lesen öffnen\n";

while($zeile=<FH>){
           if($. %10){     #Alle 10 Zeilen anhalten
               print $zeile;
               }
           else{
               print "Für weiteren Text bitte  drücken\n";
              <STDIN>;
               }
   }
close(FH);

Mit diesem einfachen Beispiel öffnen Sie die Datei $file, falls vorhanden und geben den Text in Zehn-Zeilenweise aus. Wichtig ist übrigens auch das schließen eines Datei-Handle mit close. Dies versichert, dass eventuell sich noch im Puffer befindlichen Dateien geschrieben und Abgeschlossen werden.

Vorraussetzung das Sie Dateien öffnen können, ist übrigens auch der richtige Pfadnamen und der komplette Dateiname mit Endung.

Eine weitere Möglichkeit eine Datei zu öffnen ist die Kommandozeile ...

#!/usr/bin/perl -w

$counter=0;
$file="null";
foreach (@ARGV){
         $counter++;
         if($counter > 1){    #Wurde mehr als 1 Argument angegeben
              print "\nProgramm bitte nur mit 1 Argument starten\n";
              last;
              }
         $file=$_;
        }

if($file eq "null" ){  #wurden überhaupt Argumente mit angegeben
           die "\nProgrammaufruf : programm file \n";
          }
elsif($file eq "-h" or $file eq "-help"){
           die "\nSie haben den help-Schalter aufgerufen\n";
          }

open(FH, "< $file")
         or
         die "\nKonnte die Datei $file nicht zum Lesen öffnen\n";

while($zeile=<FH>){
             if($. %10){  #Alle 10 Zeilen anhalten
                   print $zeile;
                   }
             else{
                   print "Für weiteren Text bitte drücken\n";
                   <STDIN>;
                   }
    }
close(FH);

Hier haben Sie ein kleines Beispiel wie Sie die Kommandozeile mit der Standardvariablen @ARGV auswerten können.

Jetzt fehlt eigentlich nur noch die Möglichkeit, in einer Datei etwas zu Schreiben. Dabei wird es einem recht einfach gemacht in Perl ...

#!/usr/bin/perl -w

 print "Welche Datei wollen Sie zum Anhängen öffnen : ";
 chomp( $file = <>);

open(FH, ">> $file")
          or
          die "\nKonnte die Datei $file nicht zum Anhängen öffnen\n";
print ">";

$\ = "\n";                 #Ausgabe mit mit \n beenden
$zeile=<STDIN>;      #Eingabe von STDIN
print FH $zeile;           #Eingabe in die geöffnete Datei $file schreiben

close(FH);

Hier benutzen Sie lediglich die print-Anweisung und stellen nur den File-Handle davor. Dies funktioniert natürlich auch mit printf. Die default-Einstellung von print und printf lautet ja ...

print STDOUT $variable;
printf STDOUT "$variable";

Sie benutzen bisher immer die kürzere Schreibweise da es ja die default-Einstellung ist.

Somit sind Sie nun in der Lage, Dateien zu kopieren. Das folgend Programm dürfte nun kein Problem mehr für Sie sein ...

#!/usr/bin/perl -w

print "Welche Datei wollen Sie Kopieren : ";
chomp ($quelle = <>);

print "Wie soll die Kopie heißen : ";
chomp( $ziel = <>);

open(FHquelle, "< $quelle")
        or
        die "\nKonnte die Datei $quelle nicht zum Lesen öffen\n";

open(FHziel, "> $ziel")
         or
         die "\n Konnte die Datei $ziel nicht zum Schreiben öffnen bzw. erzeugen\n";

while($zeile = <FHquelle>){           #Lesen aus der Quelle
                    print FHziel $zeile; }  #Schreiben ins Ziel

close(FHquelle);
close(FHziel);

13.2. Datei öffnen aus einem Programm           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Eine weitere Interessante Möglichkeit zum Öffnen von Dateien, stellt das Öffnen und Lesen aus laufenden Programmen da. Für open gelten in diesem Falls folgende Regeln ...

open(FH, "less $datei |") or die ".....";
open(FH, "ps -x |") or die "......";

Nun können Sie mit einem so geöffneten Programm, lesend auf die Ausgabe zurückgreifen. Das sich damit eine Unmenge an Flexibilität ergibt ist klar. Natürlich können Sie auch mehrere Programme mit Hilfe der Pipe auf einmal ausführen ...

open(FH, "less $datei | cpio -o | gzip -9 |") or die "...";

Der Nachteil an dieser Methode ist, der Rückgabewert von open() kann nicht Zuverlässig überprüft werden, da popen() (Pipe) diese Methode ermöglicht.

Sie können als Beispiel die PID unseres eigenem Programms mit Hilfe von ps ausgeben lassen ...

#!/usr/bin/perl -w
#Linux/Unix only

$pid = $$;

open(FH, "ps -x |")
     or die "Kein solche Datei vorhanden...\n";

while($tmp=<FH>){
  if($tmp =~ m/$pid/){
    print $0 , " : " , $tmp , "\n";
    }
  }

Langsam können Sie erkennen, wie mächtig aber simpel diese Methode ist. C-Programmierer unter Linux/Unix, kennen dies unter der Funktion popen(). Nun wird das Programm von oben verbessert und alle Prozesse die mit der Tastenkombination STRG+Z gestoppt wurden und alle Zombieprozesse, sollen gekillt (beendet) werden ...

#!/usr/bin/perl -w
#Linux/Unix only und geeignete Zugriffsrechte müssen vorhanden sein

open(FH, "ps -x |")
     or die "Kein solche Datei vorhanden...\n";

while($tmp=<FH>){
  if(($pid) =$tmp =~ m/(\d{4})/){ #Anpassen 4=Länge der PID!
   if($tmp =~ m/(\sT\s|\sZ\s)/){  #T=gestoppt Z=Zombie
    print $pid , " : " , $tmp , "\n";
    system("kill -9 $pid");  #killen
    }
  }
}

Das Programm soll allerdings keine Schule machen, sondern dient nur zur Demonstration.

13.3. Datei öffnen zu einem Programm           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Anders herum ist es in Perl ebenso möglich, mit open() Programme von der Standardeingabe zu füllen. Das Prinzip ist das selbe wie schon im Kapitel zuvor, nur das die Pipe (|) am Anfang des Strings steht ...

open(FH, "| programmname > $outfile") or die ".......";

Dies eignet sich zum Beispiel hervorragend, um aus einem Programm heraus, mittels sendmail ein Mail zu verschicken ...

#!/usr/bin/perl -w

open(WHERE, "which sendmail |") or die "Kein sendmail gefunden ...\n";
chomp($sendmail = <WHERE>);
close(WHERE);

$to = "root\@localhost.localdomain\n";  #Bitte anpassen
open(MAIL, "| $sendmail -oi -t $to")
      or die "Can't open pipe to $sendmail: $!\n";
print MAIL "From: Bill.Gates\@microsoft.com\n";
print MAIL "Subject: Hallo Pinguin\n\n";
print MAIL "Dies stellt den Inhalt der Mail da\n";
close(MAIL) or die "Can't close pipe to $sendmail: $!\n";

print "Die Mail wurde erfolgreich verschickt\n";

Und schon haben Sie eine Fake-Mail verschickt. Das Programm funktioniert natürlich nur wenn sich auf Ihrem Rechner auch das Programm sendmail befindet und dies entsprechend konfiguriert ist. Viele werden bei diesem Beispiel an die CGI-Programmierung denken. Und dabei haben Sie recht. Diese Beispiel werden Sie später in ähnlicher Weise bei der CGI-Programmierung, zum Verschicken einer Formmail, verwenden.

13.4. Häufig benötigte Rezepte           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

ABSATZWEISE:

#!/usr/bin/perl -w

{
  local $/ = '';        #Absatzmodus
  while(<>){      #Absatz in $_
    print;
    print "\t\t\tABSATZ - weiter mit +\n";
    <STDIN>;
   }
}

KOMPLETT EINLESEN:

#!/usr/bin/perl -w

{
  local undef $/;        #Slurp mode
  while(<>){       #Kompletter Inhalt in $_
    print;
   }
}

SATZWEISE:

#!/usr/bin/perl -w

{
  local $/=".";  #Satztrennzeichen '.' -> Bitte Anpassen!
  while(<>){
    print;
    <STDIN>;     #Weiter mit STRG+D
   }
}

Bei diesem Beispiel ist man besser mit einer Pattern-Matching-Operation beraten.

BESTIMMTE ZEILE:

#!/usr/bin/perl -w

{
  local $/ = "\n";
  $suchzeilennr=50;
  while(<>){
    print $. ,":" , $_ if $. == $suchzeilennr;
    }
}

ZEILENWEISE LESEN:

#!/usr/bin/perl -w

{
  local $/ = "\n";
  while(<>){
    print $. ,":" , $_;
   }
}

WORTWEISE LESEN:

#!/usr/bin/perl -w

undef $/;                      #Slurp mode
$_=<>;                   #alles in $_
@worte=split(m/\s/, $_);       #Aufspliten durch Whitespace-Zeichen
print "@worte";
print "\n\nElemente im Array : " , $#worte+1 , "\n";

NACH DATEIENDUNGEN SUCHEN:

#!/usr/bin/perl -w

@file = <*>;   #keine Files die mit einem '.' beginnen...
@file2= <.*>;  #Alle versteckten Files die mit einem '.' beginnen...
@file3= <*.c>; #Alle C-Files (z.B. test.c)
@file4= <* .* *.c>; #Alle zusammen.....
@file5= <*.txt>;     #alle Textdateien

print join(' ', @file) , "\n\n";
print join(' ', @file2) , "\n\n";
print join(' ', @file3) , "\n\n";
print join(' ', @file4) , "\n\n";
print join(' ', @file5) , "\n\n";

DATEIENDUNG ÄNDERN :

#!/usr/bin/perl -w

#Dateiendung verändern aus *.c wird *.c.bak
foreach $rename(<*.c>){
  rename($rename, "$rename".".bak") or warn("Fehler bei rename....\n");
  }

DATEIENDUNG WIEDER RÜCKGÄNGIG MACHEN:

#!/usr/bin/perl -w

#Dateiendung verändern aus *.c.bak wird *.c
foreach $rename(<*.bak>){
  $temp=$rename;
  $temp=~s/\.bak$//;
  rename("$rename", "$temp");
  }

13.5. Filehandle als Rückgabewert           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Um einen Filehandle aus einer Funktion zurückzugeben, müssen Sie einen Wrapper schreiben. Ein Wrapper ist ein Strumpf den man der Hauptfunktion überzieht. Folgendes Beispiel zeigt es, wie Sie dies in Perl bewerkstelligen können ...

#!/usr/bin/perl -w

sub Open{
  my $pfad=$_[0];
  local *FH;
  open(FH, $pfad) or die $!;
  return *FH;
  }

*FH_READ=Open("/usr/include/stdio.h");

while(<FH_READ>){
  print;
  }

Hier wird die Wrapper-Funktion immer mit dem selben Namen verwendet. Nur den Anfangsbuchstaben wird dabei Großgeschrieben, um diese Funktion von der echten Funktion zu unterscheiden (a la Richard W. Stevens).

Wofür steht das Sternchen am Anfang des Filehandle? Testen Sie am besten folgendes Beispiel ...

#!/usr/bin/perl -w

*var1=5;
*var2="hallo";

print *var1 , ":" , *var2 , "\n";

Anhand der Aussage dürfte nun schon einiges klar sein. Das Sternchen ist nicht speziell für Filehandles reserviert, sondern kann bei allen möglichen Variablen verwendet werden. Also kann man sich das Sternchen als ein Art Wildcard für eine Variable vorstellen, die sowohl für $, @ und % steht. Aber auch für eine Filehandle, der kein Präfix besitzt.

Man kann es sich aber auch einfacher merken, wenn man sagt, das Sternchen ist das Präfix für einen Filehandle, auch wenn es nicht ganz richtig ist.

Bei der Übergabe als Funktionsparameter funktioniert das Ganze ähnlich ...

#!/usr/bin/perl -w

sub Read{
  local *FH=shift;
  while(<FH>){print;}
 }

open(FH_READ,"/usr/include/stdio.h") or die $!;
Read(*FH_READ);

13.6. sysopen, sysread und syswrite           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

sysopen und open unterscheiden sich Hauptsächlich dadurch, dass mit sysopen die Zugriffsrechte und der Öffnungsmodus genau festgelegt werden können. Hier ein Beispiel zu sysopen ...

#!/usr/bin/perl -w

use Fcntl;

sysopen(FH, "./Neuedatei.txt", O_WRONLY|O_CREAT, 0600) or die $!;

print FH "Dieser Text wird in Neuedatei.txt geschrieben\n";
close(FH);

Anhand diese Beispiels können Sie erkennen, dass die Unterschiede zu open nicht allzu groß sind. Der Filehandle ist der selbe. Bei der Dateiangabe benötigen Sie nicht mehr die Zeichen <,>,+< usw. da Sie dies mit den O_*-Konstanten machen (welche das Modul Fcntl benötigen). Bei diesem Beispiel eben erzeugen Sie eine Datei (O_CREAT) ausschließlich zum Schreiben (O_WRONLY) mit den Zugriffsrechten 0600. Also nur für den Eigentümer. Also nochmals der Syntax zu sysopen ...

sysopen(Filehandle,Dateiname,Öffnungsmodus,Zugriffsrechte);

Weitere Zugriffsarten zu sysopen lauten ...

Modus Bedeutung
O_RDONLY nur zum lesen öffnen (O_RDONLY=0)
O_WRONLY nur zum schreiben öffnen (O_WRONLY=1)
O_RDWR zum lesen und schreiben öffnen (O_RDWR=3)
O_CREAT Falls Datei nicht existiert wird Sie neu angelegt. Falls die Datei existiert wird ist O_CREAT ohne Wirkung.
O_APPEND Datei zu Schreiben am Ende "Anhängen".
O_EXCL O_EXCL kombiniert mit O_CREAT bedeutet das die Datei nicht geöffnet werden kann wenn Sie bereits existiert und open den Wert -1 zurückliefert. (-1 == Fehler)
O_TRUNC Eine Datei die zum Schreiben geöffnet wird wird geleert. Das Nachfolgende Schreiben bewirkt erneutes Beschreiben der Datei von Anfang an. Die Attribute der Datei bleiben erhalten.
O_NOCTTY Falls der Pfadname der Name eines Terminal ist, so sollte dies nicht der Kontrollterminal des Prozesses werden.
O_NONBLOCK Falls der Pfadname der Name einer FIFO oder Gerätedatei ist, wird diese beim Öffnen und bei nachfolgenden I/O- Operationen nicht blockiert.
O_SYNC Nach dem Schreiben mit write warten, daß der Schreibvorgang vollständig abgeschlossen ist.


Mit der Funktion sysread können Sie von einem Filehandle lesen. Außerdem liest sysread die Daten Ungepuffert ein. Auch hierzu wieder ein kurzes Skript ...

#!/usr/bin/perl -w

use Fcntl;

sysopen(FH, "./Neuedatei.txt", O_RDONLY) or die $!;
$bytes=sysread(FH, $buffer,50);
print $buffer , "\n" if $bytes == 50;

close(FH);

Nun die Erklärung zu ...

$bytes=sysread(FH, $buffer,50);

Hiermit ließt man von der Datei mit dem Filehandle FH, 50 Bytes in den Puffer $buffer ein. Den Rückgabewert überprüfen Sie ebenso, ob auch wirklich 50 Zeichen an $buffer übergeben wurden.

Der Vorteil liegt auch hier wieder ganz klar bei der Performance. sysread ist bei größeren Datenmengen schneller als zum Beispiel read oder alle anderen gepufferten Einlesearten.

Das Gegenstück zu sysread ist syswrite. Auch hiermit können wir wieder ungepuffert in eine Datei schreiben. Die Anwendung von syswrite ist die selbe wie von sysread...

#!/usr/bin/perl -w

use Fcntl;

sysopen(FH, "./Neuedatei.txt", O_RDONLY) or die $!;
sysopen(BAK,"./backup.bak", O_WRONLY|O_CREAT, 0600) or die $!;

$bytes_rd=sysread(FH, $buffer,50);
$bytes_wr=syswrite(BAK, $buffer,50) if $bytes_rd == 50;
print "Fehler beim Schreiben in backup.bak\n" if $bytes_wr != 50;

close(FH);
close(BAK);

Hier wird eine Kopie von Neuedatei.txt angelegt, die Sie zuvor mit sysread auslesen. Anschließend schreiben Sie die im $buffer ausgelesenen Zeichen, in den Filehandle BAK (wieder 50 Bytes).

Sie sollten bei den sys...-Aufrufen jedoch beachten, dass Sie diese nicht mit Funktionen der Standard-Bibliothek vermischen, wegen der Unterschiedlichen Pufferung. Mit Nicht-Vermischen sind die Funktionen sysread, syswrite, systell, sysseek gemeint. Diese sollten nicht mit read, seek, tell....usw. verwendet werden.