Such Dienste up W. Wiedl: CGI- und PERL-Programmierung

Dateizugriff

Bevor man von einer Datei Lesen oder auf eine Datei schreiben kann, muß man diese Datei öffnen, dabei wird ihr ein File-Handle zugeordnet, das für die weiteren Zugriffe verwendet wird. Beim Öffnen wird bereits angegeben, ob die Datei gelesen, geschrieben oder weitergeschrieben werden soll.
Zum Lesen muß die Datei bereits existieren und das Perl-Skript muß Leseerlaubnis für diese Datei haben.
Falls eine Datei zum Schreiben geöffnet wird, die bereits existiert, muß das Perl-Skript Schreiberlaubnis für diese Datei haben, ist dies der Fall, so wird der alte Dateiinhalt überschrieben, geht also verloren. Wir eine Datei zum Schreiben geöffnet, die noch nicht existiert, wird eine neue Datei mit dem angegebenen Namen angelegt. Dazu muß das Perl-Skript Schreib- und Ausführungsrechte im betreffenden Verzeichnis haben.
Wird eine Datei zum Weiterschreiben geöffnet, die schon existiert, benötigt das Perl-Skript Schreibrechte für diese Datei. Im Gegensatz zum normalen Schreiben, bleibt der alte Inhalt jetzt erhalten, neue Sätze werden an das Dateiende angehängt. Existiert die Datei noch nicht, so wird sie neu angelegt, das Perl-Skript benötigt dazu wieder Schreib- und Ausführungsrechte für das aktuelle Verzeichnis.
Die Rechte des Perl-Skriptes sind auf unserem CGI-Server die Rechte des Besitzers des Perl-Skriptes. Wenn Sie also ein Perl-Skript geschrieben und in ihr CGI-Verzeichnis auf unserem CGI-Server abgelegt haben, arbeitet das Perl-Skript bei jedem Aufruf, egal von wem dieser Aufruf erfolgt, unter Ihrer Kennung.

Zum Öffnen einer Datei dient das Kommando

   open(FH,'Dateibezeichnung und Öffnungsmodus')
Als File-Handles FH verwendet man üblicherweise Namen, die nur aus Großbuchstaben gebildet werden, das ist aber nicht zwingend. Im Regelfalle sollte das erste Zeichen eines File-Handles ein Buchstabe sein, danach dürfen beliebig viele Buchstaben und Ziffern folgen. Perl behandelt _ als Buchstaben.

Zum Schließen einer Datei verwendet man das Kommando

   close(FH)
meist verzichtet man jedoch auf das explizite Schließen einer Datei, dies erfolgt dann implizit durch den Perl-Interpreter bei Beendigung des Skript-Laufes oder bei erneutem Öffnen derselben Datei.

Lesender Zugriff

Zum Lesen öffnet man eine Datei in einer der Formen:
   open(File-Handle,'Dateiname');
   open(File-Handle,'<Dateiname');

Das Einlesen des Dateiinhaltes kann zeilenweise erfolgen. Mit:

   $Zeile = <FH>;
wird jeweils die nächste noch nicht gelesene Zeile eingelesen und der links stehenden Variablen als Wert zugewiesen. Man kann auch alle Zeilen auf einen Schlag lesen und sie als Werte einer Liste abspeichern:
   @Zeilen = <FH>;
Beispiel:
   #!/usr/bin/perl
   open(STADT,'<../KURSDATEN/Eindaten.dat');
   $Stadt = ;
   print $Stadt;

Anzumerken ist:

Was müssen wir nun beachten, wenn wir dieses Skript von einem Formular auf dem WWW-Server aus starten wollen?

Wir erhalten dann etwa:
   #!/softsrv1/bin/perl
   print "Content-type: text/plain\n\n";
   open(STADT,'../KURSDATEN/Eindaten.dat') 
     || die "Dateiöffnung gescheitert.";
   $Stadt = <STADT>;
   print $Stadt;

Offen ist dabei noch, worauf sich die Pfadangabe ../KURSDATEN/ in der Dateibezeichnung eigentlich bezieht. Hier gilt: Das Skript wird in dem Verzeichnis ausgeführt, in dem es steht. Das Skript und das Verzeichnis KURSDATEN haben also dasselbe Elternverzeichnis.

Das Skript funktioniert folgendermaßen:

Schreibender Zugriff

Zum Schreiben öffnet man eine Datei in der Formen:
   open(File-Handle,'>Dateiname');
wenn eventuell vorhandener Dateiinhalt überschrieben werden soll und mit:
   open(File-Handle,'>>Dateiname');

wenn die neuen Formulareingaben jeweils an den vorhandenen Dateiinhalt angefügt werden sollen.

Die Ausgabe erfolgt mit der print-Anweisung:

   print FH  Ausgabedaten
Geschrieben wird damit in die aktuelle Ausgabezeile, Zeilenwechsel müssen in Perl immer explizit angegeben werden. Für Unix-Dateien ist dies das Zeichen 'newline', mit der Ersatzdarstellung \n. Man beachte, daß eingelesene Datenzeilen dieses Zeichen am Ende bereits enthalten.

Beispiel:
   #!/softsrv1/bin/perl
   open(STADT,'../KURSDATEN/Eindaten.dat') 
     || die "Dateiöffnung gescheitert.";
   $Stadt1 = <STADT>;
   $Stadt2 = <STADT>;
   print $Stadt1;
   print $Stadt1,$Stadt2,"\n";
   print $Stadt1,"\n",$Stadt2"\n";
   print "$Stadt1   $Stadt2\n";

Sperren des Dateizugriffes für andere Benutzer

Das Problem, was passiert, wenn zwei Benutzer gleichzeitig auf eine Datei zugreifen, haben wir bisher ausgeklammert. Solange beide nur lesen wollen, ist das sicher kein Problem, ein Problem kann aber entstehen, wenn einer oder beide schreiben wollen.
Mit dem flock-Befehl lassen sich solche Probleme vermeiden. Sie können mit flock die Zugriffe anderer Benutzer auf eine Datei sperren. Die Prozesse dieser Benutzer müssen solange auf den Zugriff warten, bis Ihr Prozess den Zugriff auf die Datei wieder zuläßt.

flock wird mit dem File-Handle der Datei und einer dezimalen Kennziffer aufgerufen, deren Bedeutung aus der nachfolgenden Tabelle hervorgeht.

1 LOCK_SH shared read lock. Offener Zugriffsschutz, wird meist beim lesenden Zugriff verwendet. Andere dürfen während der Sperre die Datei lesen, schreiben darf jedoch nur der Benutzer, der die Sperre veranlaßt hat.
2 LOCK_EX exclusive write lock. Exklusiver Zugriffsschutz, erlaubt anderen während der Sperre keinerlei Zugriff auf die Datei, auch keinen Lesezugriff.
4 LOCK_NB nicht blockierende Sperre. Setzt lediglich einen Vermerk auf die datei ohne andere am Zugriff zu hindern.
8 LOCK_UN unlock. Hebt eine Sperre wieder auf.

Aufgabe 2:
Schreiben Sie ein HTML-Dokument mit einem Formular, das dieselben Daten abfragt wie das Formular aus Aufgabe 1. Das Formular soll aber diesmal nicht an FormMail, sondern an ein CGI-Skript mit Namen Aufgabe2.pl in Ihrem CGI-Verzeichnis geschickt werden. Schreiben Sie ein Perl-Skript, das die Antworten entgegennimmt und Sie zeileweise in der Form:
Parametername=Parameterwert
in einer Datei in Ihrem Home-Verzeichnis auf dem CGI-Server ablegt. Nachfolgende Formulareingaben sollen immer hinten an die vorhandenen angehängt werden. Ihr Home-Verzeichnis dort hat den absoluten Pfadnamen /cgi/Kennung, wobei Kennung für Ihre Benutzerkennung steht. Die Formulareingaben sollen jeweils auch als normale Text-Datei an den WWW-Browser gehen. Abgesehen vom Abspeichern der Eingaben in einer Datei, soll Ihr Formular also so reagieren wie das nachfolgende:

Name:
MatrNr:

Ein- & Ausgabe mit Pipes

Man kann als Eingabestrom für ein Skript auch die Ausgabe eines anderen Prozesses verwenden, die Standardausgabe des anderen Prozesses wird dann in einen Eingabestrom des Perl-Skriptes gepiped. Beispiel:
   #!/softsrv1/bin/perl
   open(LS," ls -l |");
   while ($Zeile = ) {
    print $Zeile;
   }
Die Ausgabe des Unix-Kommandos 'ls -l' wird über den File-Handle LS zeilenweise eingelesen und auf die Standardausgabe ausgegeben. Das Kommando ls wirkt hier auf das Verzeichnis, in dem das Perl-Skript ausgeführt wird. Wenn also das Skript Pipe.pl heißt und im Verzeichnis: /cgi/rz2v003/cgi-bin/KURS steht und das momentane Arbeitsverzeichnis /cgi/rz2v003/cgi-bin/KURSDATEN ist, dann führt der Aufruf:
   ../KURS/Pipe.pl
dazu, daß das Inhaltsverzeichnis von /cgi/rz2v003/cgi-bin/KURSDATEN ausgegeben wird. Wenn das momentane Arbeitsverzeichnis dagegen: /cgi/rz2v003/cgi-bin/KURS ist, führt der Aufruf:
  ./Pipe.pl
dazu, daß der Inhalt von /cgi/rz2v003/cgi-bin/KURS ausgegeben wird. Wenn man das Skript von einem Formular aus aufrufen will, muß man noch den MIME-Type ausgeben lassen:
   #!/softsrv1/bin/perl
   print "Content-type: text/plain\n\n";
   open(LS," ls -l |");
   while ($Zeile = <LS>) {
    print $Zeile;
   }
Ausgegeben wird dann der Inhalt des Verzeichnisses, in dem das Perl-Skript steht.

Ebenso kann man auch einen Ausgabestrom in einen anderen Prozeß pipen. Im folgenden Beispiel wird ein Ausgabestrom an ein Drucke-Kommando weitergereicht:

   #!/softsrv1/bin/perl
   open(DR,"| lpr -Pps1");
   while (<>) {
    print DR $_;
   }
Der angegebene Drucker muß definiert sein. Auf dem CGI-Server sind keine Drucker definiert.

Beispiel: Mail versenden

   open(MAIL,"|mail ss8i000\@uni-hamburg.de");
   print MAIL "Dies ist ein Test\n";
   close(MAIL);
Man beachte, daß hier der Klammeraffe maskiert werden muß, da er für sich selbst steht und keine Listennamen kennzeichnet.

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

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");

Aufgabe3:
Schreiben Sie ein Perl-Skript, das die Eingaben des Formulares aus Aufgabe1 entgegennimmt und jeweils in eine eigene Datei in Ihrem Home-Verzeichnis auf dem CGI-Server ablegt. Der Dateiname soll gleich dem eingegebenen Nachnamen sein.

Aufgabe4:
Schreiben Sie ein Perl-Skript, das von einem Formular den absoluten Pfadnamen eines Verzeichnisses auf dem CGI-Server übernimmt und die Namen aller Dateien in diesem Verzeichnis an den WWW-Browser übergibt.

Aufgabe5:
Ändern Sie das Perl-Skript aus Aufgabe 4 so ab, daß die Dateiamen in einer Auswahlliste eines Formulares auf dem Bildschirm angeboten werden. Dateien, deren Namen mit . beginnen sollen nicht angeboten werden. Es sollen nur echte Dateien, also z.B. keine Verzeichnisnamen angeboten werden. Die ausgewählte Datei soll an das CGI-Skript: http://cgi.rrz.uni-hamburg.de/~rz2v003/KURS/Echo.pl geschickt werden.