




  Die ersten zwei Wochen in diesem Buch haben das Thema Eingabe und Ausgabe 
(auch als E/A oder I/O für input/output bezeichnet) wiederholt am Rande gestreift. 
Sie kennen <STDIN> und print für die Standardein- und -ausgabe und wissen, dass 
man die Dateieingabe über die Befehlszeile, den <>-Operator und die while-Schleife 
vornimmt, damit jede Zeile automatisch der Variablen $_ zugewiesen wird.
In dem heutigen Kapitel möchte ich Ihr Wissen über die Ein- und Ausgabe vertiefen, Ihnen noch ein bißchen mehr über die Argumentlisten für Skripts erzählen und Ihnen zeigen, wie Sie Daten und Optionen in Ihre Skripts einlesen. Heute erfahren Sie:
@ARGV arbeitet
   
 Getopt verwendet, um Schalter zu verwalten
   
  Schon zu Beginn dieses Buches, in Kapitel 3, »Weitere Skalare und Operatoren«, habe 
ich Ihnen im Zusammenhang mit der Standardeingabe und -ausgabe ein wenig über 
Datei-Handles erzählt. Damals habe ich Ihnen erklärt, dass STDIN und STDOUT eine 
besondere Art von Datei-Handle darstellen, die sich auf Ein- und Ausgabestreams 
beziehen, die - statt mit einer Datei - mit der Tastatur beziehungsweise dem Bildschirm 
verbunden sind. Aber zum Glück läßt sich vieles von dem, was Sie bisher gelernt haben, 
auch auf Datei-Handles übertragen, die sich auf das Dateisystem beziehen.
  In diesem Abschnitt lernen Sie, wie Sie die tückischen Datei-Handles in den Griff 
bekommen: wie man sie mit der open-Funktion erzeugt, Daten aus ihnen ausliest oder 
einliest beziehungsweise anhängt und wie man die Dateien wieder schließt, nachdem 
sie ihren Dienst getan haben. Außerdem werden wir nebenbei wiederholen, was wir 
bisher über Eingabe und Ausgabe gelernt haben.
Um Eingaben aus einer Quelle einzulesen oder Ausgaben an ein Ziel zu schreiben, benötigt man einen Datei-Handle. Ein Datei-Handle ist in der Regel mit einer bestimmten Datei auf der Festplatte verbunden, die zum Lesen oder Schreiben von Daten dient. Er kann sich aber auch auf eine Netzwerkverbindung (Socket), eine Pipe (eine Art Verbindung zwischen Standardeingabe und Standardausgabe, auf die wir noch am Tag 18, »Perl und das Betriebssystem«, eingehen) oder sogar bestimmte Hardware-Geräte beziehen. Das Konzept der Datei-Handles sorgt dafür, dass alle diese Operationen in gleicher Weise ausgeführt werden und Sie in gleicher Weise vorgehen können - egal woher die Daten kommen oder wohin sie gehen.
  Perl stellt Ihnen drei Standard-Datei-Handles zur Verfügung, von denen Sie bereits 
zwei kennen: STDIN, STDOUT und STDERR. Die ersten zwei stehen für die 
Standardeingabe und Standardausgabe (in der Regel Tastatur und Bildschirm). STDERR 
ist die Standardfehlerausgabe und wird für Fehlermeldungen und andere Nachrichten 
genutzt, die nicht Teil der eigentlichen Skriptausgabe sind. Normalerweise werden 
STDERR-Nachrichten wie bei STDOUT auf dem Bildschirm ausgegeben. Nur 
Programmen, die speziell Gebrauch von der Standardausgabe machen (zum Beispiel 
Programme auf der anderen Seite von Unix-Pipes), wird hier ein Unterschied 
auffallen.
Sie brauchen diese Datei-Handles weder zu öffnen noch zu initialisieren. Sie können sie so, wie sie sind, verwenden (wie bereits in den Lektionen der letzten Woche geschehen).
  Um aus einer Datei aus- oder einzulesen, müssen Sie zuerst mit Hilfe der Funktion 
open für die betreffende Operation einen Datei-Handle erzeugen. Die open-Funktion 
öffnet eine Datei zum Einlesen (Eingabe) oder zum Schreiben (Ausgabe) von Daten 
und verbindet diese Datei mit einem Datei-Handle (dessen Namen Sie frei vergeben 
können). Beachten Sie, dass es sich beim Einlesen aus einer Datei und beim 
Schreiben in eine Datei um getrennte Operationen handelt, die beide einen eigenen 
Datei-Handle benötigen.
  Die open-Funktion übernimmt zwei Argumente: den Namen eines Datei-Handles und 
die zu öffnende Datei (dazu gehört auch ein besonderer Code, der anzeigt, ob die 
Datei zum Lesen oder Schreiben geöffnet wird). Hier einige Beispiele:
open(FILE, 'meinedatei');
open(CONFIG, '.scriptconfig');
open(LOG, '>/home/www/logfile');
Den Namen des Datei-Handles können Sie beliebig wählen. Die Konvention sieht jedoch vor, dass er aus Großbuchstaben besteht und Buchstaben, Zahlen oder Unterstriche enthält. Im Gegensatz zu den Variablen muss ein Datei-Handle mit einem Buchstaben beginnen.
Das zweite Argument ist der Name der Datei auf der Festplatte, die mit Ihrem Datei- Handle verbunden werden soll. Ein einfacher Dateiname ohne weitere Pfadangaben wird im aktuellen Verzeichnis gesucht (entweder das, in dem Ihr Skript ausgeführt wird, oder ein anderes Verzeichnis, sollten Sie es gewechselt haben). Alles weitere zum Navigieren von Verzeichnissen erfahren Sie in Kapitel 17, »Umgang mit Dateien und Verzeichnissen«.
  Wollen Sie statt einfacher Dateiangaben Pfadnamen verwenden, sollten Sie Vorsicht 
walten lassen - die Pfadnotation ist von Plattform zu Plattform unterschiedlich. Unter 
Unix werden die einzelnen Pfade durch Schrägstriche (/) getrennt, wie das letzte 
unserer obigen Beispiele zeigt.
  Auf Windows-Systemen läßt sich problemlos die Standard-DOS-Notation mit 
Backslash (\) zwischen den Verzeichnissen verwenden, solange Sie daran denken, den 
ganzen Pfadnamen in einfache Anführungszeichen zu setzen. Zur Erinnerung: Ein 
Backslash gehört zu den Sonderzeichen in Perl. Wenn Sie also die Anführungszeichen 
fortlassen, erhalten Sie unter Umständen einen seltsamen Pfad, der keinen Bezug zur 
Realität hat. Und wenn Sie den String in doppelte Anführungszeichen setzen, müssen 
Sie vor den Backslash ein Escape-Zeichen (ebenfalls ein Backslash) setzen, um ein 
korrektes Ergebnis zu erzielen:
open(FILE, 'c:\temp\nummern'); # korrekt
open(FILE, "c:\temp\nummern");
# ooh! enthält einen Tabulator und eine Neue Zeile (\t, \n)
open(FILE "c:\\temp\\nummern"); # korrekt
Da die meisten modernen Windows-Systeme auch Pfadnamen mit Schrägstrichen verstehen, sollten Sie vielleicht besser diese verwenden. Damit erhöhen Sie außerdem gleichzeitig die Portierbarkeit auf Unix-Systeme (falls Ihnen daran gelegen ist).
Beim Macintosh ist das Trennzeichen zwischen den Verzeichnissen ein Doppelpunkt, und der absolute Pfadname beginnt mit der Festplatte oder dem Laufwerksbezeichner (Festplatte, CD-ROM, Diskette und so weiter). Streben Sie die Portierung auf andere Systeme an, sollten Sie sich eine Notiz machen und Ihre Pfadnamen später konvertieren. Hier ein paar Beispiele für die Mac-Syntax:
open(FILE, "Meine Festplatte:Perl:config");
open(BOOKMARKS, "HD:System Folder:Preferences:Netscape:Bookmarks.html");
  In jedem dieser Beispiele haben wir ein Datei-Handle geöffnet, um die Eingaben in 
das Skript einzulesen. Dies entspricht dem Standard. Wollen Sie die Ausgabe in eine 
Datei zurückschreiben, benötigen Sie dafür ebenfalls einen Datei-Handle, den Sie  mit 
open öffnen - allerdings unter Verwendung eines besonderen Zeichens vor dem 
Dateinamen:
open(OUT, ">output");
Das Zeichen > zeigt an, dass dieser Datei-Handle zum Schreiben von Daten geöffnet wird. Die gegebene Datei wird geöffnet und der aktuelle Inhalt, sofern vorhanden, gelöscht. (Um das Überschreiben bestehender Dateien zu vermeiden, können Sie in einem Test abfragen, ob eine Datei existiert, bevor Sie sie zum Schreiben von Daten öffnen - im Abschnitt »Datei-Tests« erfahren Sie mehr darüber.)
Wie verfahren Sie, wenn Sie Eingaben von einer Datei auslesen wollen, diese Daten dann bearbeiten und anschließend in dieselbe Datei zurückschreiben wollen? Dann müssen Sie zwei Datei-Handles öffnen: einen Handle zum Einlesen der Eingabe und später den zweiten Handle, um die Datei erneut zu öffnen und die Daten zurückzuschreiben. Lesen und Schreiben sind unterschiedliche Prozesse und bedürfen unterschiedlicher Datei-Handles.

Es sei angemerkt, dass es auch eine Syntax gibt, mit der man ein und dieselbe Datei für das Lesen und Schreiben öffnen kann:
"+>filename". Diesen Code können Sie verwenden, wenn Sie die Datei als eine Datenbank verstehen, die Sie nicht als Ganzes einlesen, sondern auf der Festplatte speichern und dann auf diese Datei zum Lesen und Schreiben zugreifen, wenn Sie Daten lesen oder ändern. In diesem Buch beschränke ich mich darauf, einfache Textdateien zu lesen und zu schreiben, so dass die Verwaltung der Daten mit Hilfe von zwei getrennten Datei-Handles weniger verwirrend und leichter ist: ein Handle, um die Daten in den Speicher einzulesen, und ein zweiter Handle, um die Daten wieder in die Datei zu schreiben.
  Sie können eine Datei auch zum Anhängen öffnen - dabei bleibt der aktuelle Inhalt 
der Datei erhalten, und alle Ausgaben über das Ausgabe-Datei-Handle werden an das 
Ende der Datei angehängt. Um Daten anzuhängen, müssen Sie die Sonderzeichen >> 
in Ihrem open-Aufruf verwenden:
open(FILE, ">>logdatei");
  Die Funktion open wird fast immer zusammen mit einem logischen or und einem 
Aufruf von die auf der anderen Seite aufgerufen:
open(FILE, "dieDatei") or die "dieDatei wurde nicht gefunden\n";
  Der Aufruf von die (»sterben«) ist nicht erforderlich, erfolgt aber so häufig in Perl, dass 
die Kombination fast unzertrennlich wirkt. Wenn Sie die Kombination nicht 
verwenden, ist es sehr wahrscheinlich, dass Sie irgend jemand, der Ihren Code sieht, 
eines Tages darauf anspricht und fragt, warum Sie den Befehl fortgelassen haben.
  »Öffne diese Datei oder stirb« ist die implizierte Drohung dieser Anweisung, und das ist 
normalerweise genau das, was Sie auch wollen. Der open-Befehl könnte fehlschlagen 
- sei es, dass Sie eine Datei zum Lesen öffnen, die nicht existiert, oder dass Ihre 
Festplatte sich seltsam verhält und die Datei nicht öffnen kann oder dass sonstige 
unvorhersehbaren Ereignisse eintreten. Normalerweise wollen Sie nicht, dass Ihr 
Skript einfach weiter ausgeführt wird, wenn etwas so absolut falsch läuft und das 
Skript nichts zum Einlesen finden kann. Zum Glück liefert die open-Funktion undef 
zurück, wenn sie die Datei nicht öffnen kann (und 1 wenn es ihr gelungen ist), so dass 
Sie dieses Ergebnis abfragen können und damit eine Entscheidungshilfe haben, was zu 
tun ist.
  Manchmal kann die Art, in der Sie auf den Fehler reagieren wollen, vom Skript 
abhängen. In der Regel jedoch wollen Sie einfach nur das Skript mit einer 
Fehlermeldung verlassen. Und genau dafür sorgt die die-Funktion: Sie beendet 
automatisch das gesamte Perl-Skript und gibt ihr Argument (eine String-Nachricht) 
über den Datei-Handle STDERR (normalerweise der Bildschirm) aus.
  Wenn Sie ein Neue-Zeile-Zeichen an das Ende der Nachricht setzen, wird Perl diese 
Nachricht beim Beenden des Skripts ausgeben. Wenn Sie das Neue-Zeile-Zeichen 
weglassen, gibt Perl als zusätzliche Information die aktuelle Zeilennummer aus: at 
script.pl line nn. Dabei ist script.pl der Name Ihres Skripts und line nn die 
Zeile, in der die ausgelöst wurde. Diese Information kann beim späteren Debuggen 
Ihres Skripts sehr nützlich sein.
  Der Befehl die kann aber noch mehr: Die spezielle Perl-Variable $! enthält den 
letzten Fehler des Betriebssystems (falls Ihr Betriebssystem einen solchen erzeugt hat). 
Indem Sie die Variable $! in den String für die einbinden, kann Ihre Fehlermeldung 
unter Umständen noch mehr Informationen liefern, als nur mitzuteilen, dass die Datei 
nicht geöffnet werden konnte. So kann zum Beispiel die folgende Version von die:
die "Datei konnte nicht geoeffnet werden: $!\n";
  in Kombination mit der Variablen folgende Meldung ausgeben: »Datei konnte nicht 
geoeffnet werden: Zugriff verweigert.« Hier erfährt der Benutzer gleich, dass die 
Datei nicht geöffnet wurde, weil die Zugriffsberechtigung für die Datei fehlte. Die 
Verwendung von $! ist immer dann zu empfehlen, wenn Sie die als Antwort auf ein 
Art von Systemfehler aufrufen.
  Obwohl die meist zusammen mit open-Aufrufen verwendet wird, sollte man daraus 
nicht schließen, dass die nur in diesem Kontext sinnvoll einzusetzen ist. Sie können 
die (und seine weniger radikale Entsprechung warn) überall dort in Ihrem Skript 
einsetzen, wo Sie die Ausführung des Skripts abbrechen wollen (oder eine Warnung 
ausgeben wollen). Weitere Informationen zu die und warn finden Sie in der perlfunc-
Manpage.
  Angenommen Sie haben einen Datei-Handle, und er ist mit einer Datei verbunden, 
die Sie zum Lesen geöffnet haben. Um Daten über den Datei-Handle einzulesen, 
verwenden Sie den (Eingabe-)Operator <> zusammen mit dem Namen des Datei-
Handles:
$line = <FILE>;
  Kommt Ihnen sicherlich bekannt vor, oder? Bei STDIN sind Sie genauso vorgegangen, 
um eine über die Tastatur eingegebene Zeile einzulesen. Das ist das Tolle an den 
Datei-Handles - Sie können genau die gleichen Prozeduren für das Lesen aus einer 
Datei wie für das Lesen von der Tastatur oder von einer Netzwerkverbindung zu 
einem Server verwenden. Perl macht da keinen Unterschied. Das Verfahren ist immer 
dasselbe, und alles was Sie bisher über E/A gelernt haben, können Sie auch für die 
Arbeit mit Dateien verwenden.
In einem skalaren Kontext liest der Eingabeoperator eine einzige Zeile bis zum Neue- Zeile-Zeichen ein:
$line = <STDIN>;
if (<FILE>) { print "weitere Eingaben..." };
  Eine besondere Möglichkeit für die Verwendung des Eingabeoperators in einem 
skalaren Kontext besteht darin, den Operator innerhalb des Tests einer while-Schleife 
zu verwenden. Damit erreichen Sie, dass bei jedem Schleifendurchlauf jeweils eine 
Zeile eingelesen und diese Zeile dann der Variablen $_ zugewiesen wird. Die Schleife 
hört erst auf, wenn das Ende der Eingabe erreicht ist:
while (<FILE>) { 
   # ... die Zeilen der Datei (in $_) verarbeiten
}
  Die gleiche Notation ist Ihnen bereits häufiger begegnet, allerdings mit leeren 
Eingabeoperatoren. Die leeren Eingabeoperatoren <> stellen in Perl einen Sonderfall 
dar. Wie Sie gelernt haben, verwendet man die leeren Eingabeoperatoren, um den 
Inhalt von Dateien einzulesen, die in der Befehlszeile des Skripts angegeben werden. 
In einem solchen Fall öffnet Perl die Dateien für Sie und sendet Ihnen deren Inhalt 
Datei für Datei über den Datei-Handle STDIN. Sie selbst brauchen nichts weiter zu tun. 
Natürlich könnten Sie auch jede Datei selbst öffnen und einlesen, aber die <>-
Operatoren in der while-Schleife stellen eine wirklich praktische Hilfe dar, die den 
Prozeß erheblich verkürzen.
In einem Listenkontext erreicht man mit den Eingabeoperatoren, dass die gesamte Eingabe auf einmal eingelesen wird, wobei jede Zeile der Eingabe einem Element der Liste zugeordnet wird. Seien Sie vorsichtig, wenn Sie den Eingabeoperator in einem Listenkontext verwenden, da er sich nicht immer so verhält, wie Sie es erwarten. Hierzu einige Beispiele:
@input = <FILE>; # liest die gesamte Datei nach @input ein;
$input = <FILE>; # liest die erste Zeile der Datei nach $input ein
($input) = <FILE>; # liest die erste Zeile der Datei nach $input ein und
# verwirft den Rest der Datei!
print <FILE>; # gibt den gesamten Inhalt von <FILE> auf dem Bildschirm
# aus
  Um Ausgaben in einen Datei-Handle zu schreiben, verwendet man meist die 
Funktionen print oder printf. (Es gibt zwar noch die write-Funktion, doch wird 
diese meist in Kombination mit Ausgabeformaten verwendet - ein Thema, das wir 
erst in Kapitel 20, »Was noch bleibt«, ansprechen werden).
  Die Funktion print (wie auch printf) geben ihre Daten standardmäßig an den Datei-
Handle STDOUT aus. Um einen anderen Datei-Handle anzusprechen, zum Beispiel um 
eine Zeile in eine Datei zu schreiben, müssen Sie erst den Datei-Handle zum 
Schreiben öffnen:
open(FILE, ">$meinedatei") or
die "Kann die Datei $meinedatei nicht finden\n";
  Und dann verwenden Sie print mit dem Datei-Handles als Argument, um Daten in 
dieser Datei abzulegen:
print FILE "$zeile\n";
  Die Funktionen printf und sprintf sind in ihrer Funktionsweise ähnlich; denken Sie 
nur daran, vor dem Formatierstring und den auszugebenden Werten den Datei-Handle 
anzugeben, in den die Ausgabe erfolgen soll:
printf(FILE "%d Antworten wurden erfaßt\n", $total / $count);
  Einen Punkt dürfen Sie bei der Verwendung von print und printf nicht vergessen: 
Es steht kein Komma zwischen dem Datei-Handle und der Liste der Dinge, die 
ausgegeben werden sollen. Dies ist einer der häufigsten Perl-Fehler (der allerdings 
aufgefangen wird, wenn Sie die Perl-Warnungen eingeschaltet haben). Das Argument 
des Datei-Handles ist vollkommen getrennt zu sehen von dem zweiten Argument, bei 
dem es sich um eine Liste von Elementen handelt, die durch Kommata getrennt sind.
Bisher haben wir in diesem Buch nur Textdaten gelesen und geschrieben. Aber nicht alle Dateien, mit denen Sie in Perl konfrontiert werden, liegen im Textformat vor. Häufig hat man es mit binären Dateien zu tun. Wenn Sie Perl unter Unix oder auf einem Mac verwenden, macht das keinen großen Unterschied, denn Unix und MacPerl verarbeiten Text- und Binärdateien ohne Probleme. Arbeiten Sie hingegen unter Windows, erhalten Sie unleserliche Ergebnisse, wenn Sie versuchen, eine binäre Datei in einem normalen Perl-Skript zu verarbeiten.
  Glücklicherweise kann man dem leicht abhelfen: Die Funktion binmode übernimmt als 
Argument einen einzelnen Datei-Handle und verarbeitet ihn (aus ihm lesen und in ihn 
schreiben) in binärer Form:
open(FILE, "meineDatei.exe") or
die " Datei meineDatei konnte nicht geoeffnet werden: $!\n";
binmode FILE;
while (<FILE>) { # im binären Modus lesen...
  Wenn Sie mit dem Lesen oder Schreiben der Daten über den Datei-Handle fertig sind, 
sollte er geschlossen werden. Meist wird Ihnen diese Aufgabe abgenommen, denn 
wenn die Ausführung Ihres Skripts beendet ist, schließt Perl alle Ihre Datei-Handles für 
Sie. Und wenn Sie mit open den gleichen Datei-Handle mehrmals hintereinander 
öffnen (zum Beispiel um einen Datei-Handle, aus dem zuvor gelesen wurde, zum 
Schreiben zu öffnen), trägt Perl dafür Sorge, dass der Datei-Handle automatisch 
geschlossen wird, bevor Sie ihn erneut öffnen. Es gehört jedoch zum guten 
Programmierstil, seine Datei-Handles zu schließen, nachdem sie ihren Dienst erfüllt 
haben. Dann belegen Sie in Ihrem Skript auch keinen unnötigen Speicher mehr.
  Einen Datei-Handle schließt man mit close:
close FILE;
Mailboxen für E-Mails gehören zu den Formaten, die Perl wirklich gut beherrscht. Jede Nachricht folgt einem speziellen Format (auf der Basis des Protokolls RFC822), und alle Nachrichten zusammen werden in einer Mailbox mit ebenfalls speziellem Format gesammelt. Wenn Sie also eine Mailbox sichten und Nachrichten, die bestimmte Kriterien erfüllen, in irgendeiner Weise verarbeiten wollen, dann ist Perl Ihre Sprache. Wie man E-Mails filtert, werden wir uns in Kapitel 21, »Ein paar längere Beispiele«, noch genauer anschauen.
  Das Beispiel in diesem Kapitel ist noch sehr einfach: Das Skript übernimmt eine 
Mailbox als Argument aus der Befehlszeile, liest die Nachrichten einzeln ein, extrahiert 
alle Zeilen, die mit subject beginnen (englisches Wort, mit dem die Betreff-Zeile der E-
Mail beginnt) und legt in dem gleichen Verzeichnis eine Datei namens Betreff an, die 
eine Liste der Betreffzeilen enthält. Existiert in dem Verzeichnis bereits eine Datei 
dieses Namens, wird sie überschrieben (im Anschluß an diesen Abschnitt zeige ich 
Ihnen, wie Sie testen können, ob eine gleichlautende Datei existiert, und dann eine 
Warnung ausgeben lassen).
Listing 15.1 enthält den (sehr einfachen) Code.
Listing 15.1: Das Skript subject.pl
1: #!/usr/bin/perl -w
2: use strict;
3:
4: open(OUTFILE, ">Betreff") or
die " Datei Betreff konnte nicht geoeffnet werden: $!\n";
5:
6: while (<>) {
7: if (/^Subject:/) {
8: print OUTFILE $_;
9: }
10: }
11: close OUTFILE;
  Ein kurzes Skript, werden Sie denken, das kaum als Beispiel taugt. Aber genau das ist 
der springende Punkt: Zum Auslesen von Daten aus Dateien und zum Schreiben in 
Dateien verwenden Sie die gleichen Techniken wie für die Standardeingabe und -
ausgabe. Auf zwei Dinge möchte ich Ihre Aufmerksamkeit lenken: Erstens, Zeile 4 
öffnet die Datei Betreff zum Schreiben (beachten Sie dabei das Zeichen > am Anfang 
des Dateinamens) und zweitens, Zeile 8 übergibt unsere Ausgabe dem gleichen Datei-
Handle OUTFILE und nicht der Standardausgabe.
  Dieses Skript erzeugt zwar keine sichtbare Ausgabe, aber wenn Sie es auf einer Datei 
mit abgespeicherten E-Mails ausführen und dann die Datei Betreff betrachten, finden 
Sie dort Zeilen wie die folgenden (mein Beispiel hier ist das Ergebnis einer Analyse 
meiner »Business-Mail«):
Subject: FREE SOFTWARE TURN$ COMPUTER$ INTO CA$H MACHINE$!!
Subject: IBM 33.6 PCMCIA Modem $89.00
Subject: 48 MILLION Email Leads $195 + BONUSES
Subject: Re: E-ALERT: URGENT BUY RECOMMENDATION
Subject: Make $2,000 - $5,000 per week -NOT MLM
Subject: Email your AD to 57 MILLION People for ONLY $99
Subject: SHY?.....................................
Subject: You Could Earn $100 Every Time the Phone Rings!!
Das Öffnen von Dateien zum Einlesen oder Hineinschreiben ist gut und schön, wenn Sie die Dateien, mit denen Sie arbeiten, kennen - zum Beispiel wissen, dass sie alle vorhanden sind oder dass Sie nicht Gefahr laufen, wichtige Daten zu überschreiben. Manchmal jedoch wollen Sie in Perl die Eigenschaften einer Datei prüfen, bevor Sie sie öffnen, oder eine Datei in Abhängigkeit von ihren diversen Eigenschaften in der einen oder anderen Weise verarbeiten.
  Perl kennt eine (ziemlich umfangreiche) Reihe von Tests für die verschiedenen 
Eigenschaften von Dateien. Mit Hilfe dieser Tests läßt sich leicht feststellen, ob eine 
Datei bereits existiert, ob Sie Daten enthält, zu welcher Art von Datei sie gehört oder 
wie alt sie ist (»1996 war ein gutes Jahr für binäre Dateien, nicht wahr?«). Diese Tests 
erinnern alle an Schalter (-e, -R, -o und so weiter), sie sollten aber nicht mit diesen 
verwechselt werden (die Ähnlichkeit ist eine Folge davon, dass sie alle ihren Ursprung 
in der Unix-Shell-Skriptsprache haben).
  Tabelle 15.1 gibt einen Überblick über einige der nützlicheren Dateitests. In der 
perlfunc-Manpage sind unter dem Eintrag -X alle Tests aufgeführt (allerdings sind 
nicht alle Optionen für alle Plattformen verfügbar).
  Jeder dieser Tests kann als Argument einen Dateinamen oder einen Datei-Handle 
übernehmen; beides ist möglich (wenn Sie jedoch überprüfen wollen, ob es eine 
bestimmte Datei gibt oder nicht, werden Sie mit Sicherheit den Dateinamen 
übergeben, um den Test vor dem Aufruf von open durchzuführen).
  Fast alle Tests liefern entweder wahr (1) oder falsch (»«) zurück. Nur -e, -s, -M und -A 
liefern andere Werte. Der Test -e liefert undef zurück, wenn die Datei nicht existiert, 
-s liefert die Anzahl der Bytes (Zeichen) in der Datei, und die Zeitoperatoren -M und -A 
liefern die Anzahl der Sekunden, die seit der letzten Änderung beziehungsweise dem 
letzten Zugriff verstrichen sind.
  Angenommen Sie wollten das Skript subject.pl dahingehend ändern, dass für den 
Fall, dass eine Datei Betreff bereits existiert, der Benutzer mit einer 
Eingabeaufforderung selber entscheiden kann, ob die Datei überschrieben werden soll 
oder nicht. Anstelle des bisherigen einfachen Aufrufs von open könnten Sie einen Test 
durchführen, um sicherzugehen, dass die Datei überhaupt existiert, und wenn ja, den 
Benutzer entscheiden lassen, ob diese überschrieben werden soll oder nicht. Schauen 
Sie sich dazu den folgenden Code an:
if (-e 'Betreff') {
    print 'Datei existiert bereits.  Überschreiben (J/N)? ';
    chomp ($_ = <STDIN>);
    while (/[^jn]/i) {
        print 'J oder N, bitte: ';
        chomp ($_ = <STDIN>);
    }
    if (/n/i) { die "Die Datei Betreff existiert bereits; Abbrechen.\n"; }
}
  In diesem Beispiel sehen Sie eine andere Anwendungsmöglichkeit für die Funktion 
die - diesmal ohne die Funktion open. Wenn der Benutzer die Frage zum 
Überschreiben der Datei mit N beantwortet, könnten Sie das Skript einfach verlassen. 
Die die-Funktion beendet das Skript ausdrücklich und gibt eine entsprechende 
Nachricht aus.
  Einen Aspekt bei der Ausführung von Perl-Skripten, den ich in den letzten Tagen 
etwas vernachlässigt habe, betrifft den Umgang mit Befehlszeilenargumenten. Sie 
haben schon ein wenig über Perls eigene Schalter (-e, -w und so weiter) erfahren, wie 
aber gehen Sie vor, wenn Sie solche Schalter oder Argumente Ihren eigenen Skripten 
übergeben wollen - wie kann man diese verarbeiten? Dieser Abschnitt behandelt im 
besonderen folgende Themen: Skriptargumente im allgemeinen und den Einsatz von 
Skriptschaltern.
  Wenn Sie ein Perl-Skript nicht nur mit dem Namen des Skripts, sondern mit weiteren 
Argumenten aufrufen, werden diese Argumente in einer besonderen globalen Liste, 
der @ARGV-Liste, gespeichert (für Mac-Droplets enthält @ARGV die Namen der Dateien, 
die auf das Droplet gezogen wurden). Sie können dieses Array genauso verarbeiten 
wie jede andere Liste in Ihrem Perl-Skript. Sehen Sie im folgenden ein Codefragment, 
das lediglich die Argumente, mit denen das Skript aufgerufen wurde, ausgibt, und 
zwar ein Argument pro Zeile:
foreach my $arg (@ARGV) {
   print "$arg\n";
}
  Wenn Ihr Skript ein Konstrukt wie while (<>) verwendet, zieht Perl den Inhalt der 
@ARGV-Liste als Dateinamen zum Öffnen und Lesen heran (stehen keine Dateien in 
@ARGV, versucht Perl, von der Standardeingabe zu lesen). Mehrere Dateien werden 
hintereinander geöffnet und gelesen, als ob es eine einzige große Datei wäre.
  Wenn Sie mehr Kontrolle über den Inhalt der Dateien, die Sie in Ihr Skript einlesen, 
haben wollen, können Sie die Namen der zu öffnenden und zu lesenden Dateien aus 
der @ARGV-Liste auslesen. Das Auswerten von @ARGV bietet sich auch dann an, wenn 
Sie nach bestimmten Argumenten suchen - zum Beispiel einer Konfigurationsdatei 
und einer Datendatei. Wollen Sie hingegen den Inhalt einer beliebigen Anzahl von 
Dateien bearbeiten, ist es praktischer, die Abkürzung <> zu verwenden. Und wenn Sie 
einen bestimmten Satz von Argumenten erwarten und kontrollieren wollen, wie diese 
verarbeitet werden sollen, lesen Sie die Dateien von @ARGV aus, und bearbeiten Sie sie 
einzeln.

Im Gegensatz zu
argv, wie es C und Unix kennen, enthält die@ARGV-Liste von Perl nur die Argumente und nicht den Namen des Skripts selber ($ARGV[0]enthält das erste Argument). Um den Namen des Skripts zu ermitteln, können Sie die spezielle Variable$0verwenden.
  Ein typischer Anwendungsbereich für die Skript-Befehlszeile ist die Übergabe von 
Schaltern an ein Skript. Schalter sind Argumente, die mit einem Gedankenstrich 
beginnen (-a, -b, -c) und in der Regel dazu dienen, das Verhalten des Skripts zu 
steuern. Manchmal bestehen Sie nur aus einem Buchstaben (-s), manchmal sind sie 
zu Gruppen zusammengefaßt (-abc), und manchmal ist ihnen ein Wert oder 
Argument zugeordnet (-o ausgabe.txt).
  Sie können ein Skript mit jedem beliebigen Schalter aufrufen. Die Schalter werden, 
wie alle anderen Argumente auch, als Elemente im @ARGV-Array aufgenommen. Wenn 
Sie das Array @ARGV mit <> bearbeiten, müssen Sie die Schalter im Array erst 
loswerden, bevor Sie irgendwelche Daten auslesen - sonst würde Perl davon 
ausgehen, dass es sich bei -s um einen Dateinamen handelt. Um alle Schalter, die 
über das ganze @ARGV-Array verstreut sind, zu bearbeiten und zu entfernen, könnten 
Sie mühselig das Array durchgehen und versuchen herauszufinden, welche Elemente 
davon Optionen  und welche Optionen mit dazugehörigen Argumenten sind. Als 
Endergebnis bliebe dann eine Liste der eigentlichen Dateinamen. Sie könnten sich 
aber auch des Moduls Getopt bedienen und sich damit die Arbeit abnehmen lassen.
  Das Modul Getopt, das als Teil der Standard-Modul-Bibliothek zusammen mit Perl 
ausgeliefert wird, dient der Verwaltung der Skriptschalter. Eigentlich handelt es sich 
um zwei Module: Getopt::Std für die Bearbeitung von Schaltern, die aus einem 
Buchstaben bestehen (-a, -d, -odatei und so weiter), und Getopt::Long, das fast 
alle erdenklichen Optionen akzeptiert, einschließlich Optionen, die aus mehreren 
Buchstaben bestehen (-sde), oder Optionen im GNU-Stil mit doppeltem Bindestrich 
(--help, --size und so weiter).
  In diesem Abschnitt bespreche ich das Modul Getopt::Std für die einfachen 
Optionen. Wenn Sie für komplexere Optionen das Modul Getopt::Long einsetzen 
möchten, sollten Sie auf die Dokumentation zu diesem Modul zurückgreifen (Details 
finden Sie in der perlmod-Manpage).
  Für eine erfolgreiche Arbeit müssen Sie das Modul Getopt::Std, wie jedes andere 
Modul auch, in Ihr Skript importieren:
use Getopt::Std;
  Durch den Import von Getopt::Std erhalten Sie zwei Funktionen: getopt und 
getopts. Diese Funktionen werden benötigt, um die Schalter aus Ihrem @ARGV-Array 
herauszuziehen und für jeden dieser Schalter in Ihrem Skript eine Skalarvariable zu 
setzen.

Das Modul
Getoptfunktioniert in der Anwendungsversion von MacPerl nicht ordnungsgemäß, da es in MacPerl keine einfache Möglichkeit gibt, Befehlszeilenschalter einzulesen. Im Abschnitt »Skriptschalter auf dem Macintosh« finden Sie Hinweise, wie Sie dieses Problem in MacPerl umgehen.
  Beginnen wir mit der Funktion getopts, die einbuchstabige Schalter mit oder ohne 
Werte definiert und bearbeitet. getopts übernimmt ein einziges String-Argument, das 
die Zeichen für die Schalter enthält, die von Ihrem Skript akzeptiert werden sollen. 
Argumente, die Werte übernehmen, müssen von einem Doppelpunkt (:) gefolgt 
werden. Groß- und Kleinbuchstaben machen einen Unterschied und definieren 
verschiedene Schalter. Schauen wir uns ein Beispiel an:
getopts('abc');
  Das in diesem Beispiel verwendete Argument 'abc' bearbeitet die Schalter -a, -b 
oder -c in einer beliebigen Reihenfolge und ohne zugeordnete Werte. Die Schalter 
können auch zusammengefaßt werden: -ab oder -abc lassen sich genauso verwenden 
wie die einzelnen Schalter. Noch ein Beispiel:
getopts('ab:c');
  Hier kann der Schalter -b einen Wert übernehmen, der auf der Perl-Befehlszeile direkt 
nach dem Schalter folgen muss:
% meinskript.pl -b 10
  Das Leerzeichen hinter dem Schalter ist nicht erforderlich. -b10 läßt sich genauso 
schreiben wie -b 10. Sie können die Schalter sogar verschachteln, solange der Wert 
nur nach dem korrekten Schalter erscheint:
% meinskript.pl -acb10 # OK
% meinskript.pl -abc10 # falsch, b und 10 gehören zusammen
  Für jeden in getopts definierten Schalter erzeugt getopts einen Skalarvariablen-
Schalter mit dem Namen $opt_x, wobei x der Buchstabe des Schalters ist (in unserem 
Beispiel würde getopts drei Variablen $opt_a, $opt_b und $opt_c erzeugen). Der 
Anfangswert jeder Skalarvariablen ist 0. Wenn der Schalter in den Argumenten zu 
dem Skript enthalten ist (in @ARGV steht), setzt getopts den Wert der zugeordneten 
Variable auf 1. Erwartet ein Schalter einen Wert, weist getopts den Wert aus @ARGV 
der Skalarvariablen für die Option zu. Danach werden der Schalter und sein 
dazugehöriger Wert aus dem Array @ARGV gelöscht. Nachdem getopts seine 
Bearbeitung abgeschlossen hat, ist Ihr @ARGV entweder leer oder enthält die restlichen 
Argumente in Form von Dateinamen, die Sie dann mit den Datei-Handles oder mit <> 
verarbeiten können.
  Nachdem getopts seine Aufgabe erledigt hat, steht Ihnen für jeden Schalter eine 
Variable zur Verfügung, die entweder den Wert 0 hat (für einen unbenutzten Schalter), 
1 (für einen benutzten Schalter) oder einen bestimmten Wert (für einen Schalter, der 
einen Wert erfordert). Sie können diese Werte abfragen und Ihr Skript, je nachdem 
mit welchem Schalter es aufgerufen wurde, verschiedene Operationen ausführen 
lassen:
if ($opt_a) {  # -a wurde verwendet
   ...
}
if ($opt_b) {  # -b wurde verwendet
 ...
}
Wenn beispielsweise der Aufruf Ihres Skripts wie folgt aussieht:
% skript.pl -a
  wird getopts('abc') die Variable $opt_a auf 1 setzen. Würde das Skript 
folgendermaßen aufgerufen:
% skript.pl -a -R
  wird $opt_a auf 1 gesetzt und der Schalter -R einfach gelöscht, ohne dass eine 
Variable gesetzt wurde. Bei folgendem Aufruf des Skripts:
% skript.pl -ab10
  und gleichzeitigem Aufruf von getopts mit
getopts('ab:c');
  wird $opt_a auf 1 gesetzt und $opt_b auf 10.
  Denken Sie daran, dass Perl bei Verwendung des Befehls use strict die plötzlich 
auftauchenden Variablen $opt_ monieren wird. Das läßt sich jedoch vermeiden, 
indem Sie diese Variablen im voraus mit use vars wie folgt deklarieren:
use vars qw($opt_a $opt_b $opt_c);
  Es gilt zu beachten, dass getopts das Array @ARGV der Reihe nach ausliest und die 
Bearbeitung unterbricht, wenn es auf ein Element stößt, das nicht mit einem 
Gedankenstrich (-) beginnt oder das keinen Wert für eine vorausgehende Option 
darstellt. Das bedeutet, dass Sie beim Aufruf eines Perl-Skripts zuerst die Optionen 
und dann die anderen Argumente aufführen sollten. Andernfalls droht Ihnen, dass 
Optionen unbearbeitet bleiben und Sie Fehler erhalten, weil versucht wird, Dateien zu 
lesen, die gar keine Dateien sind. Sie können Ihr Skript natürlich auch so schreiben, 
dass sichergestellt ist, dass @ARGV leer ist, nachdem getopts abgearbeitet ist, oder dass 
die übriggebliebenen Argumente nicht mit einem Gedankenstrich beginnen.
  Grundsätzlich sind die von getopts definierten Schalter die einzigen Schalter, die Ihr 
Skript akzeptiert. Wenn Sie ein Skript mit einem Schalter aufrufen, der nicht in dem 
Argument zu getopts definiert ist, gibt getopts einen Unknown option-Fehler aus 
(»unbekannte Option«), löscht die Option aus @ARGV und liefert falsch zurück. Dieses 
Verhalten können Sie sich zunutze machen, um sicherzustellen, dass Ihr Skript korrekt 
aufgerufen wird, und um im anderen Falle mit einer Meldung das Skript zu beenden. 
Dazu muss der Aufruf von getopts lediglich innerhalb eine if-Anweisung erfolgen:
if (! getopts('ab:c')) {
   die "Aufruf: meinskript -a -b:c datei\n";
}
  Bedenken Sie auch, dass im Falle, dass getopts die Bearbeitung Ihrer Schalter 
aufgrund eines Fehlers mittendrin abbricht, alle Schaltervariablen, die zuvor gesetzt 
wurden, ihre Werte beibehalten, auch die inkorrekten Werte. Je nachdem wie robust 
Ihre Argumentenprüfung sein soll, können Sie diese Werte auch für den Fall 
überprüfen, dass getopts falsch zurückliefert (oder ganz abbricht).
  Die Funktion getopt gleicht der Funktion getopts insofern, als beide ein 
Stringargument übernehmen, in dem die Schalter definiert sind, jedem dieser 
Argumente eine Variable $opt_ zuweisen und diese dann aus @ARGV entfernen. 
Zwischen getopt und getopts gibt es jedoch drei wesentliche Unterschiede:
getopt ist ein String mit Schaltern, denen ein Wert zugeordnet 
sein muss.
   
 getopt erübrigt sich die Definition von Argumenten ohne Werte. Jede 
einbuchstabige Option ist  erlaubt und für jede wird  eine $opt_-Variable erzeugt.
   
 getopt liefert keinen (nützlichen) Wert zurück und gibt auch keine Fehlermeldung 
für unerwartete Optionen aus.
   
  Angenommen Ihr Aufruf an getopt lautet wie folgt:
getopt('abc');
  Diese Funktion geht davon aus, dass Ihr Skript mit einer beliebigen Kombination der 
drei Schalter -a, -b oder -c aufgerufen wird, wobei jedem Schalter ein Wert 
zugewiesen wird. Wird das Skript mit Schaltern aufgerufen, die keinen Wert haben, 
sollten Sie von getopt keine Warnung erwarten - statt dessen weist es der Variablen 
für den Schalter freudig das nächste Element in @ARGV zu, auch wenn das nächste 
Element ein anderer Schalter oder ein Dateiname ist, der als Datei ausgelesen werden 
sollte. Es obliegt Ihnen, herauszufinden, ob die Werte korrekt sind oder ob das Skript 
mit dem falschen Satz an Argumenten aufgerufen wurde.
  Im Grunde genommen liegt der Hauptunterschied zwischen getopt und getopts 
darin, dass Sie bei getopt Ihre Optionen nicht deklarieren müssen, was jedoch die 
Fehlerbehandlung wesentlich erschwert. Ich ziehe in den meisten Fällen getopts vor, 
da ich so unnötiges Wertetesten vermeiden kann.
  Auf dem Mac gibt es keine Skript-Befehlszeile und deshalb können MacPerl-Skripts 
auch keine Befehlszeilenschalter annehmen, wie das bei Unix oder bei Windows-
Skripten möglich ist (über Droplets können sie jedoch Dateinamenargumente 
entgegennehmen). Das Problem läßt sich auf mehreren Wegen umgehen: So könnten 
Sie zum Beispiel am Anfang Ihres Skripts zur Eingabe von Schalter auffordern und die 
Eingabe in @ARGV speichern (so dass Sie getopt verwenden können, um die Schalter 
innerhalb Ihres Skripts zu verarbeiten). Das folgende Codefragment zeigt hierzu ein 
Beispiel:
print "Schalter eingeben: ";
chomp($in = <STDIN>);
@ARGV = split(' ', $in);
  Eine etwas ausgefeiltere Version, die das MacPerl-Modul und ein Dialogfeld 
verwendet, finden Sie als Teil von MacPerl FAQ unter http://www.perl.com/CPAN-
local/doc/FAQs/mac/MacPerlFAQ.html:
if( $MacPerl::Version =~ /Application$/ ) {
        # Ausführung als Anwendung
        local( $cmdLine, @args );
        $cmdLine = &MacPerl::Ask( "Befehlszeilenargumente eingeben:" );
        require "shellwords.pl";
        @args = &shellwords( $cmdLine );
        unshift( @ARGV, @args );
    }
Der wahre Mac-Benutzer umgeht die Befehlszeilenschalter, indem er MacPerl-Module verwendet, um eigene Dialoge zu erstellen, in denen die Schalter als echte Benutzerschnittstellenelemente implementiert werden. Ein paar einfache Dialoge werden wir in Kapitel 18 besprechen. Weitere Informationen zu den Dialogen finden Sie in der MacPerl-Dokumentation.

Wenn Sie die MPW-Version von MacPerl verwenden, können Sie das bisher Gesagte vergessen, denn hier haben Sie eine Befehlszeile. Also nutzen Sie sie!
Im folgenden sehen Sie ein einfaches Beispiel (Listing 15.2), das eine Datei auf unterschiedliche Arten bearbeitet. Wie die Datei konkret verarbeitet wird, hängt von den verwendeten Schaltern ab.
Listing 15.2: Das Skript schalter.pl
1: #!/usr/bin/perl -w
2: use strict;
3: use Getopt::Std;
4: use vars qw($opt_r $opt_l $opt_s $opt_n);
5:
6: if (! getopts('rlsn')) {
7: die "Aufruf: schalter.pl -rlsn\n";
8: }
9:
10: my @file = <>;
11:
12: if ($opt_s) {
13: @file = sort @file;
14: }
15:
16: if ($opt_n) {
17: @file = sort {$a <=> $b} @file;
18: }
19:
20: if ($opt_r) {
21: @file = reverse @file;
22: }
23:
24: my $i = 1;
25: foreach my $line (@file) {
26: if ($opt_l) {
27: print "$i: $line";
28: $i++;
29: } else {
30: print $line;
31: }
32: }
  Dieses Skript verwendet nur einfache Schalter ohne Werte (beachten Sie den Aufruf 
von getopts in Zeile 6; es stehen keine Doppelpunkte nach den Optionen). Die 
Schalter lauten -r, um den Inhalt der Datei umzudrehen, -s, um die Zeilen der Datei 
alphabetisch zu sortieren, -n, um die Zeilen numerisch zu sortieren, und -l, um die 
Zeilennummer auszugeben. Sie können die Optionen in der Befehlszeile kombinieren, 
auch wenn manche Kombinationen nicht besonders sinnvoll sind (-sn sortiert die 
Datei und sortiert sie dann erneut numerisch).
  In Zeile 4 werden die Variablen vorab deklariert, so dass Sie nicht plötzlich aus dem 
Nichts auftauchen, wenn sie mit getopts erzeugt werden (und Beschwerden durch use 
strict auslösen).
  Der Test in den Zeilen 6 bis 8 stellt sicher, dass das Skript mit den richtigen Optionen 
aufgerufen wird. Rutscht dabei eine undefinierte Option mit durch (zum Beispiel -a 
oder -x), dann bricht das Skript ab und gibt eine entsprechende Meldung aus.
  Anschließend wird in verschiedenen if-Anweisungen geprüft, ob die Variablen 
$opt_r, $opt_l, $opt_s und $opt_n existieren, und dann werden je nach aufgerufener 
Option auf der Befehlszeile verschiedene Operationen durchgeführt. Alle Argumente, 
die keine Schalter sind, verbleiben nach dem Aufruf von getopts in @ARGV und werden 
mit dem Operator <> in Zeile 10 in das Skript eingelesen.
In dieser Lektion habe ich Ihnen die Grundlagen der Ein- und Ausgabe vermittelt und gezeigt, wie man Dateisysteme verwaltet. Das, was Sie hier gelernt haben, sollte sich auf alle Ihre Perl-Programme, die Dateien und Befehlszeilenargumente verwenden, übertragen lassen. In Kapitel 17 werden wir einen eingehenderen Blick auf das Dateisystem selbst werfen. In dem Vertiefungsabschnitt dieser Lektion möchte Ihnen aufzeigen, wo Sie weitere Informationen und Details zu fortgeschritteneren Aspekten der Ein- und Ausgabe und dem Umgang mit dem Dateisystem finden.
  Alle vordefinierten Perl-Funktionen sind, wie ich bereits gesagt habe, in der perlfunc-
Manpage dokumentiert. Von Nutzen können aber auch die FAQs zu Dateien und 
Formaten in der Hilfsdokumentation perlfaq sein.
  Hier noch einige weitere Kurzschreibweisen und Eigenheiten der open-Funktion:
  Sie können den Dateinamen beim Aufruf der open-Funktion weglassen, wenn - und 
nur in diesem Fall - einer Skalarvariablen, die den gleichen Namen wie das Datei-
Handle trägt, bereits der Name der zu öffnenden Datei zugewiesen wurde. Zum 
Beispiel:
$FILE = "meinedatei.txt";
open(FILE) or die "Datei $FILE kann nicht geöffnet werden: $!\n";
Dies kann für Dateinamen, die geöffnet oder erneut geöffnet werden müssen, nützlich sein. Sie können die Variable zu Beginn Ihres Skripts setzen und dann den Dateinamen immer wieder verwenden.
  Im Gegensatz zu dem, was ich Ihnen zu Beginn dieses Kapitels gesagt habe, können 
Sie eine Datei auch gleichzeitig zum Lesen und Schreiben öffnen. Verwenden Sie 
dazu das Sonderzeichen +> vor den Dateinamen:
open(FILE, "+>diedatei") or die "Datei kann nicht geöffnet werden: $!\n";
Da dies jedoch oft nur Verwirrung stiftet, ziehe ich es vor, getrennte Datei-Handles zu verwenden und das Lesen und Schreiben als getrennte Operationen zu betrachten.
  Dateinamen, die mit einem Pipe-Zeichen (|) beginnen, fungieren als Befehl, und die 
Ausgabe wird über die Befehls-Shell Ihres Systems an diesen Befehl geleitet.
  Die Fülle an Möglichkeiten, die Ihnen open bietet, finden Sie detailliert in der 
Hilfsdokumentation perlfunc-Manpage beschrieben.
Tabelle 15.2 enthält weitere vordefinierte dateibezogene Funktionen, die ich in dieser Lektion noch nicht beschrieben habe.
Tabelle 15.2: Weitere E/A-Funktionen
Die bisher in diesem Kapitel beschriebenen Eingabe- und Ausgabetechniken umfassen die einfache, zeilenorientierte gepufferte Ein- und Ausgabe über Datei-Handles, der Standard-Ein-/-Ausgabe oder der Standardfehlerausgabe. Wenn Sie an den fortgeschrittenen Möglichkeiten der Ein- und Ausgabe interessiert sind, sollten Sie die Dokumentation der verschiedenen anderen E/A-Funktionen, die Perl für Sie bereithält, lesen. Eine Übersicht finden Sie in Tabelle 15.3.
Tabelle 15.3: Weitere E/A-Funktionen
  Außerdem gibt es noch das Modul POSIX, das weitere Möglichkeiten für 
fortgeschrittene E/A-Operationen bietet (leider läßt sich dieses Modul nur unter Unix 
einsetzen). Weitere Informationen zu POSIX finden Sie in der perlmod-Manpage.
  Perl bietet auch Unterstützung für Berkeley-Unix-DBM-Dateien (Datenbankdateien). 
Diese Dateien sind in der Regel kleiner und schneller anzusprechen als reine textbasierte 
Datenbanken. Weitere Informationen zu DBM finden Sie unter dem DB_File-Modul, 
der tie-Funktion und den diversen Tie-Modulen (Tie::Hash, Tie::Scalar und so 
weiter).
CPAN enthält eine Reihe von Modulen für die Arbeit mit Datenbanken - sei es, dass Sie selbst Datenbanken erstellen wollen, sei es, dass Sie Schnittstellen und Treiber für den Zugriff auf kommerzielle Datenbanken wie Oracle und Sybase benötigen. Für letztgenannte seien die Pakete DBD (Datenbanktreiber) und DBI (Datenbankschnittstelle) der Perl Database Initiative wärmstens empfohlen.
  Dateien und Verzeichnisse können mit sogenannten Zeitmarkierungen versehen 
werden. Dabei handelt es sich um Angaben, wann die Datei erzeugt, geändert oder 
zuletzt darauf zugegriffen wurde. Mit Hilfe von Dateitests (-M für Änderungen, -A für 
Zugriff und -C für Änderungen an den inode-Informationen1) können Sie die 
Zeitmarkierungen überprüfen, mit Hilfe der stat-Funktion erhalten Sie detailliertere 
Informationen über die Zeitmarkierungen, und mit der utime-Funktion läßt sich die 
Zeitmarkierung einer Datei ändern. Das Verhalten der Tests und Funktionen kann 
dabei von Plattform zu Plattform variieren.
  Alle Zeitangaben erfolgen in Sekunden, gemessen ab einem bestimmten Zeitpunkt; 
für Unix und Windows ist das der 1. Januar 1970, für den Macintosh der 1. Januar 
1904. Zum Decodieren und Ändern von Zeitmarkierungen können auch die 
Funktionen time, gmtime, localtime und die Module Time::Local nützlich sein.
In diesem Kapitel haben wir das, was Sie bisher über die Ein- und Ausgabe gelernt haben, vertieft und ergänzt. Dabei haben wir die Techniken, die beim Lesen aus der Standardeingabe und beim Schreiben in die Standardausgabe zur Anwendung kamen, auf das Einlesen und Schreiben von Dateien übertragen.
  Zu Beginn standen Dateien und Datei-Handles im Zentrum unserer Betrachtung. Ich 
habe Ihnen gezeigt, wie Sie die Funktion open verwenden, um eine Datei zu öffnen 
und ein Datei-Handle zu erzeugen, über das Sie aus dieser Datei lesen oder in die 
Datei schreiben können. In Zusammenhang mit open haben Sie die Funktion die 
kennengelernt, die das Skript beendet und dabei eine Fehlernachricht an die 
Standardfehlerausgabe schickt.
  Im weiteren Verlauf der Lektion sprachen wir über Skript-Argumente und Schalter: 
was passiert, wenn Sie ein Skript mit Argumenten aufrufen (sie werden in @ARGV 
abgelegt), und wie Sie vorgehen, um diese Argumente zu bearbeiten. Enthalten diese 
Argumente Schalter, bearbeiten Sie sie am besten mit dem Modul Getopt::Std. 
Damit können Sie Schalter für Ihr Skript definieren und bearbeiten und dann mit Hilfe 
spezieller Variablen prüfen, ob diese Schalter überhaupt existieren.
Unter anderem haben Sie folgende Funktionen in diesem Kapitel kennengelernt:
open - erzeugt Datei-Handles
   
 die - beendet das Skript mit einer Fehlernachricht
   
 binmode - setzt einen Datei-Handle in den Binärmodus
   
 close - schließt einen Datei-Handle
   
 getopts - Teil des Moduls Getopt; deklariert und bearbeitet Argumente
   
 getopt - ebenfalls Teil des Moduls Getopt; behandelt Argumente
   
Frage:
 Ich versuche, eine Datei zum Schreiben zu öffnen, aber es wird immer wieder die 
Funktion die ausgelöst, und ich kann mir nicht erklären, warum. Das Verzeichnis 
erlaubt den Lesezugriff, die Datei existiert noch nicht - es gibt keinen Grund, 
warum hier etwas schieflaufen sollte.
Antwort:
 Haben Sie daran gedacht, dass >-Zeichen vor den Dateinamen zu setzen? Sie 
brauchen dieses Zeichen, um Perl mitzuteilen, dass in die Datei geschrieben 
werden soll. Andernfalls geht Perl davon aus, dass aus dieser Datei 
ausgelesen wird, und wenn es diese Datei dann nicht findet, ist es auch nicht 
in der Lage, sie zu öffnen.
Frage:
 Ich möchte meine Datei mit einer Subroutine öffnen und dann den Datei-Handle 
an andere Subroutinen weiterreichen. Aber wenn ich das versuche, funktioniert es 
nicht. Warum?
Antwort:
 Weil es so einfach nicht geht. Man kann zwar mit Typeglobs einige trickreiche 
Kniffe anwenden, um Symbolnamen zwischen Subroutinen weiterzureichen, 
doch ist dies ein Gebiet, das wieder seine ganz eigenen Probleme birgt.  Am 
besten übergibt man Datei-Handles, indem man das Modul FileHandle nutzt, 
den Datei-Handle als Objekt erzeugt und dieses Objekt dann zwischen den 
Subroutinen weiterreicht.
Da wir jedoch noch nicht allzuviel über Objekte gelernt haben, könnten Sie (erst einmal) Ihr Datei-Handle global erzeugen und darauf in den Subroutinen Bezug nehmen. (Auf die Objekte werde ich in Kapitel 20 kurz zu sprechen kommen.)
Frage:
 Ich versuche eine einfache textbasierte Datenbank in Perl zu lesen. Ich kenne das 
Format der Datei und weiß, wie man es dekodieren muss, um etwas damit 
anfangen zu können. Trotzdem ist die Eingabe, die ich erhalte, absolut 
unverständlich. Wo liegt der Fehler?
Arbeiten Sie unter Windows? Liegt die Datenbank-Datei im binären Format vor? Verwenden Sie die Funktion
binmode, um sicherzustellen, dass Perl Ihren Datei- Handle in binärem Format liest.
Frage:
 Ich arbeite mit MacPerl. Jetzt habe ich eine Datei mit Zahlen von einem Unix-
System vor mir liegen, und wenn ich sie in mein Skript einlese, erhalte ich als 
Ergebnis einen einzigen großen String anstatt eines Arrays von einzelnen Strings. 
MacPerl ignoriert anscheinend die Neue-Zeile-Zeichen. Wie gehe ich weiter vor?
Antwort:
 Das haben Sie ganz richtig diagnostiziert: MacPerl ignoriert die Neue-Zeile-
Zeichen. Unix- und Macintosh-Systeme handhaben Zeilenendezeichen 
unterschiedlich. Unter Unix wird das Zeichen \n (Zeilenvorschub, ASCII 10) 
verwendet und unter Macintosh das Zeichen \r (Wagenrücklauf, ASCII 13). 
MacPerl ist nur zum Lesen von Macintosh-Dateien ausgelegt, so dass es ein 
Wagenrücklaufzeichen anstelle eines Zeilenvorschubs erwartet. (Falls es Sie 
interessiert, Windows/DOS verwendet beide).
Um dieses Problem zu umgehen, haben Sie zwei Möglichkeiten. Zum einen konvertieren FTP und die meisten Dateitransferprogramme heutzutage die Zeilenvorschubzeichen in Wagenrücklaufzeichen automatisch, wenn Sie eine Unix-Datei auf einen Mac verschieben. Und wenn nicht, gibt es viele Editoren, die das können. Nachdem Sie eine Unix-Datei in eine Mac-Datei konvertiert haben, dürfte das Problem nicht mehr auftauchen.
Antwort:
 	 Die andere Lösung besteht darin, ganz oben in Ihrem Skript das 
Eingabedatensatz-Trennzeichen von MacPerl in ein Zeilenvorschubzeichen zu 
ändern:
$/ = "\n";
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.
STDIN, STDOUT und 
STDERR?
 die? Warum sollten man sie zusammen mit open 
verwenden?
 while-Schleife und in 
einem Listenkontext verhalten.
 	 -e
 	 -x
 	 -f
 	 -M
 	 -Z
 @ARGV verwendet? Was enthält die Variable?
 getopt und getopts?
 getopts:
 	 getopts('xyz:');
 	 getopts('x:y:z');
 	 getopts('xXy');
 	 getopts('xyz');
 	 getopt('xyz');
gemischt.
 gemischt. Sind die Extensionen unterschiedlich, 
verlassen Sie das Skript mit einer Fehlermeldung. (HINWEIS: Verwenden Sie 
reguläre Ausdrükke, um die Extensionen der Dateinamen zu ermitteln).
 -o. Ist die Option gesetzt, sollen auch 
Dateien mit unterschiedlichen Extensionen vermischt werden (die Extension der 
Ergebnisdatei können Sie frei wählen), und es wird keine Fehlermeldung 
ausgegeben.
 -u, -s, -r und -c. -u liefert 
den String in Großbuchstaben zurück, -s entfernt Interpunktionszeichen und 
Whitespace, -r dreht den String um, und -c zählt die Anzahl der Zeichen. Stellen 
Sie sicher, dass die Optionen in verschiedenen Kombinationen verwendet werden 
können, um unterschiedliche Effekte zu erzielen.
 meinskript.pl -sz eingabedatei
$opt_z liefert nicht 
wahr zurück).
use strict;
use Getopt::Std;
use vars qw($opt_s $opt_z);
getopt('sz');
if ($opt_z) {
# ...
}
Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.
STDIN, SDTOUT und STDERR beziehen sich auf die Standardeingabe, die 
Standardausgabe und die Standardfehlerausgabe. Datei-Handles zu allen anderen 
Dateien erzeugen Sie mit der Funktion open.
 die-Funktion bricht das Skript behutsam ab und gibt dabei eine (hoffentlich) 
hilfreiche Fehlernachricht aus. Meistens wird die Funktion zusammen mit open 
verwendet, da man in dem Fall, dass eine Datei aus irgendwelchen Gründen nicht 
geöffnet werden kann, das Skript üblicherweise nicht weiter ausführen möchte. Es 
gehört daher zum guten Programmierstil, stets die Rückgabewerte von open zu 
überprüfen und die aufzurufen, falls beim Aufruf von open etwas schieflief.
 <> und den Namen des Datei-Handles. In einem skalaren 
Kontext liest der Eingabeoperator immer nur eine Zeile. Innerhalb einer while-
Schleife weist er die einzelnen Zeilen nacheinander der Variablen $_ zu. In einem 
Listenkontext wird die gesamte Eingabe bis zum Ende der Datei eingelesen.
 print-
Funktion und den Namen des Datei-Handles. Beachten Sie, dass kein Komma 
zwischen dem Datei-Handle und dem, was ausgegeben werden soll, steht.
  -e testet, ob die Datei existiert.
 	 -x testet, ob es sich bei der Datei um eine ausführbare Datei handelt 
(normalerweise nur für Unix relevant).
 	 -f testet, ob es sich bei der Datei um eine einfache Datei (und nicht ein 
Verzeichnis, einen Link oder etwas anderes) handelt.
 	 -M prüft das Bearbeitungsdatum der Datei.
 	 -Z testet, ob die Datei existiert und leer ist.
 @ARGV speichert alle Argumente und Schalter, mit denen 
das Skript aufgerufen wurde.
 getopt definiert Schalter mit Werten, akzeptiert aber jede beliebige 
Option. Die Funktion getopts deklariert die möglichen Optionen für das Skript 
und legt fest, ob diese Werte haben oder nicht. Ein weiterer Unterschied besteht 
darin, dass getopts den Wert falsch zurückliefert, wenn Fehler bei der 
Bearbeitung der Befehlszeilenschalter aufgetreten sind. getopt liefert keinen 
praktischen Wert zurück.
 	 getopts('xyz:') definiert die Schalter -x, -y und -z mit einem Wert
 	 getopts('x:y:z') definiert -x und -y mit Wert und -z ohne Wert
 	 getopts('xXy') definiert -x und -X (beides sind separate Schalter) sowie -y. 
Keiner der Schalter hat einen Wert.
 	 getopts('xyz') definiert -x, -y und -z alle ohne Werte.
 	 getopt('xyz') definiert -x, -y und -z mit Werten sowie beliebige weitere 
einbuchstabige Schalter.
#!/usr/bin/perl -w
use strict;
my ($file1, $file2) = @ARGV;
open(FILE1, $file1) or
die "Datei $file1 konnte nicht geoeffnet werden: $!\n";
open(FILE2, $file2) or
die "Datei $file2 konnte nicht geoeffnet werden: $!\n";
open(MERGE, ">gemischt") or
die "Die gemischte Datei konnte nicht geoeffnet werden: $!\n";
my $line1 = <FILE1>;
my $line2 = <FILE2>;
while (defined($line1) || defined($line2)) {
if (defined($line1)) {
print MERGE $line1;
$line1 = <FILE1>;
}
if (defined($line2)) {
print MERGE $line2;
$line2 = <FILE2>;
}
}
#!/usr/bin/perl -w
use strict;
my ($file1, $file2) = @ARGV;
my $ext;
if ($file1 =~ /\.(\w+)$/) {
$ext = $1;
if ($file2 !~ /\.$ext$/) {
die "Extensionen sind nicht identisch.\n";
}
}
open(FILE1, $file1) or
die "Datei $file1 konnte nicht geoeffnet werden: $!\n";
open(FILE2, $file2) or
die "Datei $file2 konnte nicht geoeffnet werden: $!\n";
open(MERGE, ">gemischt") or
die "Die gemischte Datei konnte nicht geoeffnet werden: $!\n";
my $line1 = <FILE1>;
my $line2 = <FILE2>;
while (defined($line1) || defined($line2)) {
if (defined($line1)) {
print MERGE $line1;
$line1 = <FILE1>;
}
if (defined($line2)) {
print MERGE $line2;
$line2 = <FILE2>;
}
}
#!/usr/bin/perl -w
use strict;
use Getopt::Std;
use vars qw($opt_o);
getopts('o');
my ($file1, $file2) = @ARGV;
my $ext;
if ($file1 =~ /\.(\w+)$/) {
$ext = $1;
if ($file2 !~ /\.$ext$/) {
if (!$opt_o) {
die "Extensionen sind nicht identisch.\n";
}
}
}
open(FILE1, $file1) or
die "Datei $file1 konnte nicht geoeffnet werden: $!\n";
open(FILE2, $file2) or
die "Datei $file2 konnte nicht geoeffnet werden: $!\n";
open(MERGE, ">gemischt") or
die "Die gemischte Datei konnte nicht geoeffnet werden: $!\n";
my $line1 = <FILE1>;
my $line2 = <FILE2>;
while (defined($line1) || defined($line2)) {
if (defined($line1)) {
print MERGE $line1;
$line1 = <FILE1>;
}
if (defined($line2)) {
print MERGE $line2;
$line2 = <FILE2>;
}
}
#!/usr/bin/perl -w
use strict;
use Getopt::Std;
use vars qw($opt_s $opt_r $opt_u $opt_c);
getopts('sruc');
my $str = $ARGV[0];
if ($opt_s) {
$str =~ s/[\s.,;:!?'"]//g;
}
if ($opt_r) {
$str = reverse $str;
}
if ($opt_u) {
$str = uc $str;
}
if ($opt_c) {
$str = length $str;
}
print "$str\n";
getopt. Im Gegensatz zu getopts 
geht diese Funktion davon aus, dass alle Schalter mit Werten versehen sind. Wenn 
also das Skript mit dem Schalter -sz aufgerufen wird, geht getopt davon aus, 
dass Sie den Schalter -s verwenden, der den Wert z hat. Der Schalter -z wird von 
getopt nie registriert. Verwenden Sie die Funktion getopts, um sicherzustellen, 
dass sowohl $opt_s und $opt_z gesetzt werden.





Die Option -C steht nur unter Unix zur Verfügung, wo jede Datei über eine inode-Datenstruktur verfügt, in der Informationen über die Datei verwaltet werden.