




In Kapitel 15, »Dateien und E/A«, haben wir uns damit beschäftigt, wie man in Perl aus Dateien liest oder in Dateien schreibt. Heute wollen wir uns weiteren Aspekten der Dateiverwaltung zuwenden - insbesondere den verschiedenen Möglichkeiten, mit denen Sie über Ihr Skript das Dateisystem beeinflussen können. Heute lernen Sie,
Mit Perl können Sie nicht nur aus Dateien lesen und in Dateien schreiben, sondern Sie können die Dateien auch verwalten - so wie Sie es üblicherweise von der Befehlszeile oder einem Datei-Manager aus tun: Sie können Dateien umbenennen, löschen, Zugriffsrechte ändern oder Verknüpfungen zu Dateien erstellen. Für jede dieser Aufgaben greifen Sie auf verschiedene vordefinierte Perl-Funktionen zurück, die ich Ihnen in diesem Abschnitt vorstellen möchte.
  Um einen Dateinamen durch einen anderen zu ersetzen, ohne dabei den Inhalt der 
Datei zu beschädigen, rufen Sie die Funktion rename mit zwei Argumenten auf: dem 
alten Namen der Datei und dem neuen Namen:
rename meinedatei, meinedatei.bak;
  Existiert bereits eine Datei mit dem gleichen Namen wie die neue Datei, wird diese 
von Perl überschrieben. Sind Sie an Unix gewöhnt, stellt dies kein Problem für Sie 
dar. Arbeiten Sie jedoch auf einem Windows- oder Mac-Rechner, gilt es vorsichtig zu 
sein! Es gibt keine Sicherheitsabfragen, wenn Sie eine bestehende Datei 
überschreiben. Deshalb sollten Sie vielleicht besser sicherstellen, dass es für den neuen 
Namen nicht bereits eine gleichnamige Datei gibt (mit einem der Dateitests wie -e), 
bevor Sie Ihre Datei umbenennen.
  Die rename-Funktion liefert 1 oder 0 zurück (wahr oder falsch), je nachdem ob Sie Ihre 
Datei erfolgreich umbenennen konnten oder nicht. Um eine Datei umbenennen zu 
können, müssen Ihnen (oder dem Benutzer Ihres Skripts) die entsprechenden 
Zugriffsrechte eingeräumt sein.
  In der Unix-Version von Perl gibt es zwei Funktionen zum Erstellen von 
Verknüpfungen zwischen Dateien: link und symlink, mit denen harte 
beziehungsweise symbolische Verknüpfungen (entsprechend dem Unix-Befehl ln) 
erstellt werden. Harte Verknüpfungen dienen dazu, eine Datei mit mehr als einem 
Namen zu erzeugen. Wenn Sie einen beliebigen dieser Namen (einschließlich der 
Originaldatei) löschen, existiert die Datei weiter - solange wie es noch Namen für die 
Datei gibt. Symbolische Verknüpfungen sind Verweise auf andere Dateien. Wird die 
Verknüpfung gelöscht, bleibt die Datei bestehen, löschen Sie hingegen die 
Originaldatei, zeigt die symbolische Verknüpfung ins Leere. Nicht alle Unix-Systeme 
unterstützen symbolische Verknüpfungen.
  Beide Funktionen, link und symlink, erfordern zwei Argumente: den Name der 
Originaldatei, für die eine Verknüpfung erstellt werden soll, und den Namen der 
neuen Verknüpfung. Sehen Sie dazu folgendes Beispiel:
link datei1, datei2;
  Beide Funktionen liefern im Erfolgsfall 1 (wahr) und ansonsten 0 (falsch) zurück.
  Weder hart noch symbolisch verknüpfte Dateien stellen für Perl ein Problem dar. 
Wenn Sie open mit einem Dateinamen verwenden, der eine Verknüpfung darstellt, 
wird statt der Verknüpfung die Originaldatei geöffnet. Mit Hilfe des Dateitests -l 
können Sie prüfen, ob eine bestimmte Datei eine symbolische Verknüpfung ist oder 
nicht. Anschließend können Sie die Verknüpfung mit Hilfe der Funktion readlink zu 
der ursprünglichen Position der Datei zurückverfolgen:
if (-l $dat) {                        # dat ist eine Verknüpfung
   $ursprungsdatei = readlink $dat;   # ermittelt die echte Datei
}
  Beachten Sie, dass Sie mit readlink nur den relativen Pfadnamen der Originaldatei zu 
ihrer symbolischen Verknüpfung erhalten. Das bedeutet, dass Sie entweder diesen 
Pfad zu einer vollen Pfadangabe erweitern oder in das Verzeichnis mit der 
symbolischen Verknüpfung wechseln müssen, um den Pfadnamen der Originaldatei 
zu verwenden. Wie Sie Verzeichnisse wechseln, erfahren Sie weiter hinten in diesem 
Kapitel.
  Verwenden Sie Perl für Windows? Dann können Sie weder mit harten noch mit 
symbolischen Verknüpfungen arbeiten. Sie können aber das Modul Win32::Shortcut 
verwenden, um Windows-Explorer-Verknüpfungen zu erzeugen.
  In MacPerl gibt es zwar keine Entsprechung zu den harten Verknüpfungen, doch 
funktionieren die symbolischen Verknüpfungen ganz analog zu den Mac-Aliasen. Die 
Funktion symlink erzeugt einen Alias, der Dateitest -l liefert wahr zurück, wenn die 
Datei ein Alias ist und readlink ermittelt die Position der Datei, mit der der Alias 
verknüpft ist.
  Sie sind mit einer Datei fertig und sind sich sicher, dass Sie sie nicht weiter benötigen. 
Wie werden Sie diese Datei jetzt wieder los? Dazu steht Ihnen die Funktion unlink zur 
Verfügung (trotz ihres Namens entfernt sie sowohl Verknüpfungen als auch Dateien). 
Sie entspricht in ihrer Funktionsweise dem Unix-Befehl rm oder dem DOS-Befehl del. 
Allerdings verschiebt diese Funktion die Datei nicht in einen Papierkorb wie unter 
Windows oder beim Mac. Wird eine Datei gelöscht, wird sie unwiederbringlich 
entfernt. Deshalb sollten Sie Vorsicht walten lassen, um nicht aus Versehen Dateien 
zu löschen, die Sie eigentlich noch benötigen.
  Die Funktion unlink übernimmt ein Listenargument, bei dem es sich entweder um 
einen einzigen Dateinamen oder um eine ganze Liste von Dateinamen handeln kann. 
Als Rückgabe liefert Sie die Anzahl der gelöschten Dateien zurück.
unlink temp, config, foo;
Beachten Sie, dass Perl auf manchen Systemen - allen voran Unix - bedenkenlos auch Nur-Lesen-Dateien löscht. Vorsicht ist also geboten, denn der Nur-Lesen-Status einer Datei bezieht sich lediglich darauf, dass der Inhalt dieser Datei nicht geändert werden darf, und nicht darauf, ob der Dateiname verschoben oder gelöscht wird.
  Wird die Funktion unlink auf eine harte Verknüpfung angewendet, wird diese 
Verknüpfung entfernt. Andere harte Verknüpfungen zu der Datei bleiben bestehen. 
Auch im Falle einer symbolischen Verknüpfung wird die Verknüpfung entfernt, die 
Originaldatei bleibt bestehen. Mit readlink können Sie symbolischen Verknüpfungen 
nachfolgen. Beachten Sie, dass nach dem Löschen einer Datei, zu der symbolische 
Verknüpfungen bestehen, alle symbolischen Verknüpfungen zu der Datei ins Leere 
zielen.
  Sie können mit unlink keine Verzeichnisse entfernen. Wie Sie Verzeichnisse löschen, 
erfahren Sie im Abschnitt »Verzeichnisse verwalten und wechseln«.
Es gibt noch eine Reihe weiterer dateibezogener Funktionen, die ich Ihnen kurz vorstellen möchte. Eine ausführlichere erspare ich mir, da sich die betreffenden Operationen von Plattform zu Plattform unterscheiden (oder unter Umständen unter Windows oder Mac gar nicht existieren). In Tabelle 17.1 sind weitere Dateiverwaltungsfunktionen aufgeführt, die auf Unix-Systemen verfügbar sind und mit Unix-Befehlen korrspondieren. Wenn Sie eine der Funktionen aus Tabelle 17.1 verwenden möchten, schauen Sie in der Dokumentation zu Ihrer Perl-Version nach, ob diese Funktion unterstützt wird oder nicht (oder ob es für die betreffende Operation anderweitig Unterstützung gibt - zum Beispiel durch ein plattformspezifisches Modul).
Weitere Informationen zu diesen Funktionen finden Sie in der perlfunc-Manpage.
Tabelle 17.1: Weitere Dateiverwaltungsfunktionen
  Um unter Windows und Windows NT Dateizugriffsberechtigungen und -attribute zu 
verwalten, können Sie auch die Module win32::File und win32::FileSecurity 
verwenden. Diese ermöglichen es Ihnen, Dateiattribute und NT-Nutzerrechte zu 
prüfen und zu ändern.
So wie Sie in Ihren Perl-Skripten mit Dateien in der gleichen Weise arbeiten, wie Sie es von der Shell oder der Befehlszeile aus tun, so können Sie in Ihren Perl-Skripten auch durch das Dateisystem navigieren und Verzeichnisse verwalten (im Sprachgebrauch mancher Betriebssysteme spricht man auch von Ordnern statt von Verzeichnissen). Sie können das aktuelle Verzeichnis ermitteln, das aktuelle Verzeichnis wechseln, den ganzen Inhalt des Verzeichnisses oder einen Teilbereich davon auflisten, und Sie können Verzeichnisse selbst anlegen oder löschen. In diesem Abschnitt möchte ich auf einige Aspekte bei der Ausführung dieser Operationen mit Perl eingehen.
Jedes Perl-Skript kennt sein aktuelles Arbeitsverzeichnis, das heißt das Verzeichnis aus dem das Skript aufgerufen wurde (wenn Sie gewohnt sind, mit einem befehlszeilenorientierten System zu arbeiten, wird Sie dies nicht überraschen). Auf dem Mac, der mit Droplet-Skripten arbeitet, ist das aktuelle Arbeitsverzeichnis das Verzeichnis, in dem sich das Droplet selbst befindet.
  Um das aktuelle Verzeichnis von einem Perl-Skript aus zu ändern, verwenden Sie die 
Funktion chdir:
chdir "bilder";
  Wie schon bei der Verwendung von Verzeichnisnamen im Aufruf der open-Funktion 
sollten Sie darauf achten, dass Sie die Pfadangaben korrekt angeben und die 
Eigenheiten der verschiedenen Systeme berücksichtigen. Relative Pfadangaben, wie in 
diesem Beispiel, sind in Ordnung. Obiges Beispiel ändert das aktuelle 
Arbeitsverzeichnis in das Verzeichnis bilder, das unter dem gleichen Verzeichnis 
angeordnet ist, aus dem das Skript ursprünglich aufgerufen wurde (puh!).
  Ist es möglich, herauszufinden, in welchem Verzeichnis Sie sich momentan befinden? 
Ja, aber wie Sie dazu vorgehen, hängt von dem System ab, auf dem Sie gerade 
arbeiten. Unter Unix stellen Sie das aktuelle Verzeichnis fest, indem Sie die schrägen 
Anführungsstrich-Operatoren verwenden, um den Befehl pwd aufzurufen (mehr zu den 
schrägen Anführungszeichen in Kapitel 18, »Perl und das Betriebssystem«):
$curr =`pwd`;
  Diese Methode läßt sich auch auf dem Mac anwenden. Unter Windows erhalten Sie 
das aktuelle Arbeitsverzeichnis mit der Funktion Win32::GetCWD. Der beste Weg, das 
aktuelle Arbeitsverzeichnis zu ermitteln - ein Weg, der übrigens auf allen Plattformen 
gleich gut funktioniert - führt allerdings über das Modul Cwd, das Teil der 
Standardbibliothek ist. Das Cwd-Modul stellt Ihnen die cwd-Funktion zur Verfügung, die 
das aktuelle Arbeitsverzeichnis zurückliefert (unter Unix führt cwd übrigens einfach 
`pwd` aus, so dass Sie auch hier bezüglich des Ergebnisses sicher sein können):
use Cwd;
$curr = cwd();
  In Perl gibt es zwei Wege, eine Liste der Dateien in einem Verzeichnis erstellen zu 
lassen: Die erste Methode verwendet Platzhalter, mit deren Hilfe Sie eine Liste von 
Dateien erzeugen können, die einem bestimmten Muster entsprechen. Die zweite 
Methode verwendet die Funktionen opendir und readdir, die beide eine komplette 
Liste aller Dateien in einem Verzeichnis ausgeben.
  Diese Technik, die man im Englischen als Datei-»Globbing« bezeichnet, erlaubt es 
Ihnen, eine Liste aus Dateien des aktuellen Verzeichnisses zu erstellen, die einem 
bestimmten einfachen Muster entsprechen. Wenn Sie jemals mit einem 
befehlszeilenorientierten System gearbeitet und Dateien mit Mustern wie *.txt oder 
*foo* aufgelistet haben, dann haben Sie schon mit Datei-Globbing gearbeitet (auch 
wenn Sie vielleicht nicht wußten, dass man diese Technik so nennt).

Der Begriff »glob« ist - falls Sie es nicht schon geraten haben - von Unix entlehnt. Verwenden Sie niemals einen einfachen Begriff, wenn es auch kompliziert geht.
  Beim Datei-Globbing nutzen Sie das Platzhalterzeichen *, um einen bestimmten Satz 
von Dateinamen zu definieren. Verwechseln Sie das Datei-Globbing aber nicht mit 
den regulären Ausdrücken - beim Datei-Globbing steht das * für ein beliebiges 
Zeichen (keines oder mehrere), und es ist das einzige Sonderzeichen, das Sie 
verwenden können. Als Ergebnis erhalten Sie eine Auflistung aller Dateinamen in dem 
aktuellen Verzeichnis, die dem Muster entsprechen. So liefert zum Beispiel *.html alle 
Dateinamen zurück, die auf die Extension .html auslauten, und *foo* alle Dateien, 
die im Namen selbst die Zeichenfolge foo enthalten. Sie können diese Dateinamen 
dann verwenden, um auf den einzelnen Dateien bestimmte Operationen auszuführen.
  Perl erzeugt Datei-Globs auf zwei Wegen: mit dem Operator <>, der zwar aussieht wie 
der Eingabeoperator, aber keiner ist, oder mit der glob-Funktion.
  Wenn Sie den <>-Operator verwenden, müssen Sie das Muster innerhalb der spitzen 
Klammern angeben:
@dateien = <*.pl>;
  Dieser Ausdruck, bei dem <> in einem Listenkontext verwendet wird, liefert eine Liste 
aller Dateien mit der Extension .pl zurück. Wenn Sie <> in einem skalaren Kontext 
verwenden, erhalten Sie jeden Dateinamen separat (wie bei der Eingabe), und wenn 
Sie den Operator innerhalb einer while-Schleife verwenden, werden die Dateinamen 
nacheinander der $_-Variablen zugewiesen, ebenfalls wie bei der Eingabe).
  So gibt zum Beispiel das folgende Codebeispiel eine Liste aller Dateien in dem 
aktuellen Verzeichnis aus, die die Extension .txt haben:
while (<*.txt>) {
   print $_, "\n";
}
  Verwechseln Sie den Globbing-Operator <> nicht mit dem <>-Operator für die 
Eingabe - sie sehen nur äußerlich gleich aus, arbeiten aber in ganz unterschiedlicher 
Weise. Letzterer benötigt einen Datei-Handle als Argument und liest den Inhalt dieser 
Eingabedatei. Ersterer liefert Strings mit den Dateinamen aus dem aktuellen 
Verzeichnis, die dem Muster entsprechen.
  Da man die beiden Operatoren für das Globbing und für die Eingabe leicht 
verwechseln kann, gibt es noch die glob-Funktion. Damit wird das Globbing für alle 
deutlich und die Verwechslungsgefahr gebannt. Bei der glob-Funktion müssen Sie das 
Filtermuster in Anführungszeichen setzen:
@dateien = glob "*.pl";
  Das Ergebnis ist das gleiche. Auch hier erhalten Sie eine Liste der Dateien, die auf .pl 
enden (in einem skalaren Kontext erhalten Sie die Dateien eine nach der anderen). In 
modernen Perl-Skripten sollte man dem Datei-Globbing mit der glob-Funktion den 
Vorzug geben.
  Es gibt noch einen zweiten Weg, um eine Liste der Dateien in einem Verzeichnis zu 
erhalten, und der führt über die Verzeichnis-Handles. Diese erinnern stark an die 
Datei-Handles und verhalten sich auch so. Sie beziehen sich aber ausschließlich auf 
Verzeichnisse. Wenn Sie ein Verzeichnis mit Hilfe eines Verzeichnis-Handles einlesen, 
erhalten Sie eine vollständige Liste aller Dateien in dem Verzeichnis, einschließlich der 
versteckten Dateien, die mit einem Punkt beginnen (die Sie durch Datei-Globs nicht 
erhalten), sowie . und .. für das aktuelle und das übergeordnete Verzeichnis in Unix 
und Windows (der Mac erkennt diese Zeichen nicht als Elemente des aktuellen 
Verzeichnisses; Sie können statt dessen : für das aktuelle und :: für das 
übergeordnete Verzeichnis verwenden).
  Verzeichnis-Handles werden auf die gleiche Art und Weise geöffnet und geschlossen 
wie Datei-Handles, nur dass man die Funktionen opendir und closedir verwendet. 
Die Funktion opendir übernimmt zwei Argumente: den Namen eines Verzeichnis-
Handles (von Ihnen gewählt) und das Verzeichnis, das ausgegeben werden soll.
opendir(DIR, ".") or die "Verzeichnis kann nicht geöffnet werden: $!\n";
  Wie bei der open-Funktion sollten Sie stets das Ergebnis prüfen und das Skript 
gegebenenfalls mit Hilfe der die-Funktion in Würde sterben lassen (die Variable $! ist 
hier ebenfalls sehr hilfreich).
  Bezüglich der Namensgebung gelten für die Verzeichnis-Handles (hier DIR) die 
gleichen Regeln wie für die Datei-Handles. Verzeichnis-Handles werden vollkommen 
unabhängig von den Datei-Handles betrachtet - Sie können einem Verzeichnis-
Handle den gleichen Namen geben wie einem Datei-Handle, es wird keine Konflikte 
geben.
  Sobald ein Verzeichnis-Handle geöffnet ist, können Sie mit readdir daraus lesen. Wie 
schon der Eingabeoperator <> liefert auch readdir in einem skalaren Kontext einen 
einzelnen Dateinamen zurück und in einem Listenkontext eine Liste aller Dateinamen. 
Das folgende Codefragment öffnet zum Beispiel das aktuelle Verzeichnis (das ».«-
Verzeichnis unter Unix und Windows; Mac-Anwender schreiben statt dessen »:«) und 
gibt eine Liste des Verzeichnisses aus:
opendir(CURR, ".") or die "Verzeichnis kann nicht geöffnet werden: $!\n";
while (defined($file = readdir(CURR))) {
print "$file\n";
}
  Beachten Sie, dass readdir - im Gegensatz zum Eingabeoperator <> - keine 
automatischen Zuweisungen an die Variable $_ vornimmt. Sie müssen den Code dazu 
selbst in die while-Schleife aufnehmen oder (wie ich) eine temporäre Variable 
verwenden.
  Mit readdir erhalten Sie eine Liste aller Dateien und Unterverzeichnisse in dem 
Verzeichnis. Wenn Sie bestimmte Dateien (versteckte Dateien, . oder ..) herausfiltern 
wollen, müssen Sie dazu einen Vergleich mit einem regulären Ausdruck anschließen.
  Sobald Sie das Verzeichnis-Handle nicht mehr benötigen, können Sie es mit der 
closedir-Funktion schließen:
closedir(CURR);
  Sie können aus Ihren Perl-Skripten heraus sogar neue Verzeichnisse anlegen und 
nicht mehr benötigte Verzeichnisse löschen. Die Funktionen dazu lauten mkdir und 
rmdir.
  Die Funktion mkdir übernimmt zwei Argumente: den Namen eines Verzeichnisses 
(oder Ordners) und einen Satz an Zugriffsberechtigungen. Unter Unix beziehen sich 
die Zugriffsberechtigungen auf die Standardzugriffsbits des chmod-Befehl im 
Oktalformat (0 plus drei Zahlen von 0 bis 7). Unter Windows und Mac ist das 
Argument der Zugriffsberechtigung zwar gefordert, aber nicht sinnvoll. Verwenden 
Sie einfach 0777 als Argument, und Sie dürften keine Probleme bekommen:
mkdir temp, 0777;

0777ist eine Oktalzahl, die sich auf die Unix-Zugriffsberechtigungsbits bezieht und Lese-, Schreib- und Ausführungsberechtigungen für die Welt, die Gruppe und den Besitzer bedeutet. Auf dem Mac und unter Windows werden andere Formen von Zugriffsberechtigungen für Dateien und Verzeichnisse verwendet, so dass Sie das Argument der Zugriffsberechtigung anmkdirhier eigentlich nicht brauchen (dennoch müssen Sie einen Wert übergeben, und deshalb ist0777eine gute, allgemein sinnvolle Wahl.)
  Die mkdir-Funktion erzeugt das neue Verzeichnis in dem aktuellen Arbeitsverzeichnis 
des Skripts. Sie können das Verzeichnis aber auch überall sonstwo anlegen, indem Sie 
den vollen Pfadnamen angeben, zuerst das aktuelle Verzeichnis wechseln oder - falls 
Sie Spaß an ausgefallenem Code haben - die Möglichkeiten des Moduls File::Path 
nutzen, das Teil der Standardbibliothek ist und Funktionen zum Erstellen und Löschen 
ganzer Verzeichnisunterbereiche enthält.
  Zum Entfernen eines Verzeichnisses gibt es den Befehl rmdir.
rmdir temp;
Das Verzeichnis muss jedoch leer sein (das heißt es darf keine Dateien enthalten), damit Sie es löschen können.
  Jetzt möchte ich Ihnen ein richtig nützliches Beispiel präsentieren, das ich selbst recht 
häufig verwende: Wenn Sie ein Verzeichnis voller GIF- oder JPEG-Grafiken haben (in 
dem Verzeichnis also Dateien mit den Extension .gif, .jpeg oder .jpg stehen), 
können Sie mit Hilfe dieses Skripts eine HTML-Datei namens index.html erzeugen, 
die Verknüpfungen zu diesen Dateien enthält. Auf diesem Wege können Sie schnell 
eine große Menge an Grafikdateien in das Web stellen, ohne erst viel Zeit mit dem 
Anlegen der HTML-Datei zu verschwenden (wenn Sie möchten, können Sie die Datei 
selbstverständlich danach noch nachbessern).
  Das Skript, das ich für diese Aufgabe geschrieben habe, verwendet Datei-Handles, um 
die Datei index.html zu öffnen und in die Datei zu schreiben, Verzeichnis-Handles, 
um den Inhalt des aktuellen Verzeichnisses einzulesen, und Datei-Tests mit regulären 
Ausdrücken, um die Dateien herauszufiltern, nach denen wir suchen. In Listing 17.1 
sehen Sie das Ergebnis:
Listing 17.1: Das Skript imagegen.pl
1: #!/usr/bin/perl -w
2: use strict;
3: use Cwd;
4:
5: open(OUT, ">index.html") or
die "Index-Datei kann nicht geöffnet werden: $!\n";
6: &printhead();
7: &processfiles();
8: &printtail();
9:
10: sub printhead {
11: my $curr = cwd();
12: print OUT "<HTML>\n<HEAD>\n";
13: print OUT "<TITLE>Bilddateien aus dem Verzeichnis $curr</TITLE>\n";
14: print OUT "</HEAD>\n<BODY>\n";
15: print OUT "<H1>Bilddateien</H1>\n";
16: print OUT "<P>";
17: }
18:
19: sub processfiles {
20: opendir(CURRDIR, '.') or
die "Verzeichnis kann nicht geöffnet werden ($!),
exiting.\n";
21: my $file = "";
22:
23: while (defined($file = readdir(CURRDIR))) {
24: if (-f $file and $file =~ /(\.gif|\.jpe?g)$/i) {
25: print OUT "<A HREF=\"$file\">$file</A><BR>\n";
26: }
27: }
28: closedir(CURRDIR)
29: }
30:
31: sub printtail {
32: print OUT "</BODY></HTML>\n";
33: close(OUT);
34: }
  Wir beginnen, indem wir die Ausgabedatei (index.html) in Zeile 5 öffnen. Achten Sie 
auf das >-Zeichen, es zeigt Ihnen an, dass es sich hier um ein Handle für eine 
Ausgabedatei handelt.
  Die Subroutine &printhead() in den Zeilen 10 bis 17 gibt lediglich den oberen Teil 
der HTML-Datei aus. Die einzige Schwierigkeit dabei besteht darin, das aktuelle 
Verzeichnis in den Titel der Seite aufzunehmen. Zu diesem Zweck wird die Funktion 
cwd() aufgerufen (die in Zeile 3 aus dem Cwd-Modul importiert wurde). Ich habe hier 
auf die Verwendung eines Hier-Dokuments für die Ausgabe des HTML-Codes 
verzichtet, denn es wären dann mit Sicherheit nur noch mehr Zeilen geworden, als es 
ohnehin schon sind.
  In der Subroutine &processfiles() (Zeile 19 bis 29) findet die eigentliche Arbeit statt. 
Zeile 20 öffnet das aktuelle Verzeichnis (Mac-Anwender müssen das ».« in »:« ändern). 
Daran schließt sich in den Zeilen 23 bis 26 eine while-Schleife an, die jeden 
Dateinamen im Verzeichnis durchläuft. Der Test in Zeile 24 prüft, ob es sich bei der 
Datei auch tatsächlich um eine Datei und nicht um ein Verzeichnis handelt (der Test -
f), und filtert mit Hilfe eines regulären Ausdrücks die Grafikdateien heraus (jene mit 
der Extension .gif, .jpeg und .jpg). Handelt es sich bei der aktuellen Datei wirklich 
um eine Grafikdatei, geben wir in Zeile 25 eine Verknüpfung zu dieser Datei aus, 
wobei wir den Dateinamen als Verknüpfungstext verwenden.
Beachten Sie, dass Sie durch die Verwendung der Dateinamen als Verknüpfungstexte nicht unbedingt zu sehr aussagekräftigen Verknüpfungen kommen - wenn Sie Pech haben, lauten Ihre Verknüpfungen »bild1.gif«, »bild2.gif«, »bild3.gif« und so weiter. Nicht sehr aussagestark, aber zumindest ein Anfang. Nachdem das Skript ausgeführt wurde, können Sie ja die HTML-Datei bearbeiten und aussagekräftigere Verknüpfungen erstellen (»netter Sonnenaufgang«, »Osterumzug«, »Bill verhält sich dumm« und so weiter).
  Als Abschluß des Skripts rufen wir die Subroutine &printtail() auf, die das Ende der 
HTML-Datei ausgibt und den Ausgabedatei-Handle schließt.
Zusätzlich zu den Funktionen, die ich in dieser Lektion und in Kapitel 15 beschrieben habe und die allesamt in der perlfunc-Manpage beschrieben sind, gibt es in der Standard-Modulbibliothek noch eine Reihe weiterer Module mit Funktionen zum Verwalten von Dateien und Datei-Handles.
Viele dieser Module sind einfach objektorientierte Hüllen zu den Standard- Dateioperationen, andere wiederum sind besonders nützlich, um plattformübergreifende Probleme zu lösen oder zu umgehen. Wieder andere stellen einfach Funktionen zur Verfügung, die die Verwaltung von Dateien und Verzeichnissen leichter machen.
  Einige dieser Module habe ich bereits erwähnt - beispielsweise Getopt und Cwd. In 
Tabelle 17.2 finden Sie eine ausführlichere Liste. Details zu den einzelnen Modulen 
finden Sie in der perlmod-Manpage.
Tabelle 17.2: Dateibezogene Module
Ein Perl-Skript ist keine Insel. Es kommt der Punkt, da ist es sehr wahrscheinlich, dass Ihr Skript sich mit der äußeren Welt des Dateisystems beschäftigen muss, besonders dann, wenn Ihr Skript extensiv Dateien liest und in Dateien schreibt. In Kapitel 15 haben Sie gelernt, wie man den Inhalt einer Datei liest und in eine Datei schreibt. Heute haben wir uns den globaleren Themen der Datei- und Verzeichnisverwaltung innerhalb von Perl-Skripten gewidmet.
Begonnen haben wir mit den Dateien: wie man sie umbenennt, wie man für sie Verknüpfungen erstellt und wie man sie verschiebt - ganz so wie man mit Dateien von der Befehlszeile aus oder in einem grafischen Dateimanager arbeiten würde.
Wenn man Dateien verwalten kann, sollte man auch in der Lage sein, Verzeichnisse zu verwalten. In der zweiten Hälfte dieser Lektion ging es daher darum, wie man Verzeichnisse anlegt und löscht, das aktuelle Arbeitsverzeichnis ausgibt oder wechselt und wie man Listen von Dateien aus einem Verzeichnis einliest (entweder durch Datei-Globbing oder durch Lesen aus einem Verzeichnis-Handle).
Frage:
 Ich versuche, ein portierbares Skript zu schreiben, das eine config-Datei einliest. 
Ich kann in den Pfadnamen für Unix und Windows den Schrägstrich verwenden, 
aber der Mac mit seinen Doppelpunkten in den Pfadangaben macht die Sache 
doch sehr kompliziert. Wie kann ich das Problem lösen?
Antwort:
 Sie können mit Hilfe eines Tests feststellen, ob Sie Ihr Skript auf einem Mac 
ausführen, und dann das Pfad-Trennzeichen ordnungsgemäß einrichten (und 
Ihren Dateipfad durch das Aneinanderhängen von Strings aufbauen). 
Verwenden Sie das Config-Modul, um herauszufinden, auf welcher Plattform 
Sie sich befinden:
use Config;
if ( $Config{'osname'} =~ /^macos/i ) {
# ... Macintosh-spezifische Anweisungen
}
Frage:
 Ich arbeite auf einem Unix-Rechner. Ich habe ein Skript getestet, das den Inhalt 
einer Datei einliest und die Datei dann löscht. Da ich wollte, dass die Datei erst 
gelöscht wird, nachdem das Skript debuggt worden ist, habe ich für die Datei die 
Zugriffsberechtigung zum Schreiben gelöscht. Trotzdem hat Perl die Datei einfach 
gelöscht. Warum?
Antwort:
 Die Zugriffsberechtigungen einer Datei legen lediglich fest, ob Sie den Inhalt 
der Datei lesen und schreiben können oder nicht. Ob die Datei umbenannt, 
verschoben oder geändert werden kann, hängt von den 
Zugriffsberechtigungen des betreffenden Verzeichnisses ab. Wenn Sie in Perl 
verhindern wollen, dass Dateien gelöscht werden, müssen Sie die 
Schreibberechtigung für das Verzeichnis löschen, in dem sich die Datei 
befindet. Oder  - noch besser - kommentieren Sie einfach Ihren unlink-Befehl 
aus, bis Sie den Rest Ihres Skripts debuggt haben. Unter http://
www.perl.com/CPAN-local/doc/FMTEYEWTK/file-dir-perms finden Sie eine 
Seite, auf der dieses Problem detailliert beschrieben ist.
Frage:
 Sie haben beschrieben, wie man eine Datei umbenennt, eine Verknüpfung zu 
einer Datei erstellt und Dateien entfernt. Wie jedoch kopiert man eine Datei?
Antwort:
 Sie können dazu beide Dateien öffnen (die Datei, die kopiert werden soll, und 
die Datei, in die kopiert werden soll) und dann die Zeilen aus der einen Datei 
auslesen und in die andere ausgeben. Oder Sie nutzen die schrägen 
Anführungszeichen, um den Kopierbefehl des Systems aufzurufen (wie Sie 
noch in Kapitel 18 lernen werden). Oder Sie verwenden das Modul 
File::Copy, das Ihnen eine Kopierfunktion zur Verfügung stellt, mit der Sie 
Dateien oder Datei-Handles von einer Quelle in ein Ziel kopieren können.
Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.
`pwd` und cwd()? Warum sollten Sie die eine 
Form der anderen vorziehen?
 <*> und einer Liste 
von Dateien, die Sie mit readdir() erhalten?
 mkdir?
/
) aus.
     while (<foo*>) {
   unlink $_;
}
 #!/usr/bin/perl -w
opendir(DIR, '.') or die "Verzeichnis kann nicht geöffnet werden ($!)\n";
while (readdir(DIR)) {
print "$_\n";
}
closedir(DIR);
Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.
`pwd` verwendet schräge Anführungszeichen, um den Unix-Befehl pwd 
aufzurufen. Während manche Perl-Versionen auf anderen Systemen diesen Aufruf 
ebenfalls korrekt verarbeiten (namentlich MacPerl), gilt dies bei weitem nicht für 
alle Perl-Versionen. Soll Ihr Skript problemlos portierbar sein, verwenden Sie das 
Modul Cwd und die Funktion cwd(). Denken Sie daran, dass Sie Cwd erst 
importieren müssen (use Cwd), bevor Sie es verwenden können.
 *.pl zum Beispiel ist ein Glob, der alle 
Dateien in einem Verzeichnis zurückliefert, die die Extension .pl aufweisen. Globs 
sind besonders nützlich, wenn man einen Satz von Dateien aufgreifen und 
auslesen möchte, ohne explizit das Verzeichnis-Handle zu öffnen, die Liste zu 
lesen und die Dateien auszusortieren, die dem Muster entsprechen.
 <*> liefert alle Dateien in einem Verzeichnis, abgesehen von den versteckten 
Dateien, den Dateien, die mit  ».« starten, und den Verzeichnissen ».« und »..«. 
Verzeichnis-Handles liefern alle diese Dinge zurück.
 mkdir legt fest, welche Zugriffsberechtigung das neue 
Verzeichnis haben soll. Unter Unix folgen diese Zugriffsberechtigungen den 
normalen Format für Lese-, Schreib- und Ausführungszugriff für Besitzer, Gruppe 
oder Welt. Für Mac oder Windows ist das Argument der Zugriffsberechtigung 
ohne Bedeutung, dennoch müssen Sie es mit angeben (verwenden Sie einfach 
0777, und Sie bekommen keine Schwierigkeiten).
#!/usr/bin/perl -w
use strict;
use Cwd;
my $curr = cwd();
opendir(CURRDIR, $curr) or
die "Verzeichnis kann nicht geöffnet werden ($!)\n";
my @files = readdir(CURRDIR);
closedir(CURRDIR);
foreach my $file (sort @files) {
if (-d $file) {
print "$file/\n";
}
else { print "$file\n"; }
}
unlink ist nur für das Löschen von Dateien gedacht. Verzeichnisse 
werden mit rmdir gelöscht. Geht es darum, alle Dateien und Verzeichnisse zu 
entfernen, müssen Sie die Dateinamen einzeln daraufhin prüfen, ob es sich um 
eine Datei oder ein Verzeichnis handelt, und dann die entsprechende Funktion 
aufrufen.
 while-Schleife. Fälschlicherweise wurde 
angenommen, dass readdir in einer while-Schleife den nächsten 
Verzeichniseintrag der Variablen $_ zuweist - genauso wie dies beim Einlesen 
einer Eingabezeile von einem Datei-Handle der Fall ist. Dies stimmt jedoch nicht. 
Für readdir müssen Sie explizit den nächsten Verzeichniseintrag einer Variablen 
zuweisen:
    while (defined($in = readdir(DIR))) { ... }
 &getfilename(), die die Fehlerprüfung durchführt, um sicherzustellen, dass die 
Datei, die angegeben wurde, auch existiert (zum Umbenennen oder Löschen einer 
Datei) oder gerade nicht existiert (zum Erzeugen einer neuen Datei).
#!/usr/bin/perl -w
use strict;
my $choice = '';
my @files = &getfiles();
while ($choice !~ /q/i) {
$choice = &printmenu();
SWITCH: {
$choice =~ /^1/ && do { &createfile(); last SWITCH; };
$choice =~ /^2/ && do { &renamefile(); last SWITCH; };
$choice =~ /^3/ && do { &deletefile(); last SWITCH; };
$choice =~ /^4/ && do { &listfiles(); last SWITCH; };
$choice =~ /^5/ && do { &printhist(); last SWITCH; };
}
}
sub printmenu {
my $in = "";
print "Wählen Sie einen Befehl (or Q to quit): \n";
print "1. Datei erzeugen\n";
print "2. Datei umbenennen\n";
print "3. Datei loeschen\n";
print "4. Alle Dateien auflisten\n";
while () {
print "\nIhre Wahl --> ";
chomp($in = <STDIN>);
if ($in =~ /^[1234]$/ || $in =~ /^q$/i) {
return $in;
} else {
print "Keine gueltige Wahl. 1-4 oder Q, bitte,\n";
}
}
}
sub createfile {
my $file = &getfilename(1);
if (open(FILE, ">$file")) {
print "Datei $file wurde erzeugt.\n\n";
@files = &getfiles();
close(FILE);
} else {
warn "$file konnte nicht geoeffnet werden ($!).\n";
}
}
sub renamefile {
my $file = &getfilename();
my $in = '';
print "Geben Sie den neuen Dateinamen ein: ";
chomp($in = <STDIN>);
if (rename $file,$in) {
print "Datei $file wurde in $in umbenannt.\n";
@files = &getfiles();
} else {
warn "$file konnte nicht umbenannt werden ($!).\n";
}
}
sub deletefile {
my $file = &getfilename();
if (unlink $file) {
print "Datei $file wurde geloescht.\n";
@files = &getfiles();
} else {
warn "$file konnte nicht geloescht werden ($!).\n";
}
}
sub getfilename {
my $in = '';
# ohne Argumente aufrufen, um sicherzustellen, dass die
# Datei existiert
my $new = 0;
# mit Argument aufrufen, um sicherzustellen, dass die
# Datei nicht existiert
if (@_) {
$new = 1;
}
while (!$in) {
print "Geben Sie einen Dateinamen ein (? fuer Liste): ";
chomp($in = <STDIN>);
if ($in eq '?') {
&listfiles();
$in = '';
} elsif ((grep /^$in$/, @files) && $new) {
# Datei existiert, keine neue Datei, OK
# Datei existiert, neue Datei: Fehler
print "Datei ($in) existiert bereits.\n";
$in = '';
} elsif ((!grep /^$in$/, @files) && !$new) {
# Datei existiert nicht, neue Datei, OK
# Datei existiert nicht, keine neue Datei: Fehler
print "Datei ($in) nicht gefunden.\n";
$in = '';
}
}
return $in;
}
sub getfiles {
my $in = '';
opendir(CURRDIR, '.') or
die "Verzeichnis konnte nicht geoeffnet werden ($!),
exiting.\n";
@files = readdir(CURRDIR);
closedir(CURRDIR);
return @files;
}
sub listfiles {
foreach my $file (@files) {
if (-f $file) { print "$file\n"; }
}
print "\n";
}




