![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
||||||||||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Einführung in Perl - Teil VDateibehandlungvon Achim Schmidt |
![]() |
open ( HANDLE, "Dateiname");verbindet die Datei Dateiname mit den Hanlde HANDLE. Der Name des Filehandle ist grundsätzlich frei wählbar und benutzt, wie wir es bereits gewohnt sind einen anderen Namensraum als unsere Variablen und Feldbezeichner. Nomalerweise werden Filehandles, auch in Hinsicht auf neue reservierte Wörter, groß geschrieben.
Nun müßen wir Perl beim Anlegen eines solchen Handles nur noch sagen, ob wir die Datei zum Lesen, Schreiben oder Anhängen öffnen wollen. Hier kommen nun auch die von der Shell her bekannten Operatoren <, > und >> ins Spiel. Diese werden einfach dem Dateinamen vorangestellt. Somit weiß das Perlprogramm in welchem Modus die Datei zu öffnen ist:
Operator Beispiel Funktion < open ( EIGDAT, "< test"); öffnet die Datei test zum Lesen > open ( EIGDAT, "> test"); öffnet die Datei test zum Schreiben >> open ( EIGDAT, ">> test"); öffnet die Datei test zum AnhängenMittels des zugewiesenen Filehandles wird die geöffnete Datei nun im Verlauf des Programmes angesprochen, aber das sehen wir uns am besten anhand eines kleinen Beispieles an:
Programm: demo5-1.pl #!/usr/bin/perl open (TESTDAT, "< test"); while ($zeile=<TESTDAT>) { print $zeile; } close (TESTDAT);Dieses kleine Programm öffnet die Datei test, liest es innerhalb der Schleife Zeile für Zeile in die Variable $zeile ein und gibt diese anschließend aus. Am Ende der Datei liefert der Zugriff auf das Filehandle false, wodurch die Schleife beendet wird. Danach wird die Datei mittels der close () Anweisung wieder geschloßen.
Die einfachste Form, die die () Anweisung zu verwenden, sehen Sie in Listing demo5-2.pl, in welchem die Prüfung auf erfolgreiche Dateiöffnung einfach mittels des || (oder) Operators gemacht wird.
Programm: demo5-2.pl #!/usr/bin/perl open (TESTDAT, "< test") || die "Fehler beim Öffnen von test" ; while ($zeile=<TESTDAT>) { print $zeile; } close (TESTDAT);Sehen wir uns nun das Ganze doch einmal an einem etwas sinnvolleren Beispiel an. Das Programmpaket netacct von Ulrich Callmeider (uc@brian.lunetix.de), welches den Netzverkehr "accountet", erstellt eine Logdatei mit folgender Syntax:
786476591 6 193.98.158.1 119 192.76.152.9 2072 5370 eth0 unknownDabei sind:
Programm: demo5-3.pl #!/usr/bin/perl #################################################### ## ## NetAcct Auswertungsprogramm ## as@Saar.DE ## #Programmnamen erkennen @cmd = split ('/', $0); $aufruf = $argv0 = @cmd[$#cmd]; # Aufrufparameter checken if (@ARGV[1]) { # Wenn Parameter da, Auswertung starten $logdatei = @ARGV[0]; $ipaddress = @ARGV[1]; # Logdatei öffnen open ( LOGDAT, "< $logdatei") || die "Fehler beim Öffnen von $logdatei" ; $traffic = 0; while (chop($zeile = <LOGDAT>)) { # Datei zeilenweise durchgehen @acctinfo = split ('\t', $zeile); # Zeile in Array splitten if (($acctinfo[2] eq $ipaddress) || ($acctinfo[4] eq $ipaddress)) { $traffic += $acctinfo[6]; # Traffic accounten wenn IP Adresse stimmt } # End-of-IF } # End-of-WHILE print "Traffic von $ipaddress in $logdatei: $traffic bytes\n"; close (LOGDAT); # Datei schließen } else { # Wenn Parameter fehlen, usage - Meldung ausgeben print "usage: $aufruf <Netacct-Logdatei> <IP-Adress to account>\n\n"; }
Das Programm geht zunächst hin und merkt sich den Namen, unter welchem es aufgerufen wurde, um ggf. eine korrekte Usagemeldung auszugeben. Anschließend wird geprüft, ob die beiden benötigten Parameter (Name der zu verwendenden Logdatei und der zu accountenden IP Adresse) existieren. Dies ist mittels des Arrays @ARGV möglich ist. Dies enthält (natürlich bei 0 beginnend) einen einzelnen Aufrufparameter und stellt Sie dem Perlprogramm bereit. Existieren also zwei Übergabeparameter startet der eigentliche Programmlauf, anderenfalls wird die Usagemeldung im else Zweig der großen If-Schleife ausgeführt. Im weiteren Programmverlauf wird nun als nächstes die Logdatei zum Lesen geöffnet und die eingelesene Zeile in ein Array aufgeteilt (mittels split ()). Aufgeteilt wird nach Tabulatorzeichen, nachdem dies die Feldtrenner der Logdatei sind, bietet es sich wohl an :-).
Als weiteres wird nun mittels der IF Anweisung geprüft ob der dritte ($acctinfo[2]) oder der fünfte ($acctinfo[4]) Skalar des Arrays (also Quell- oder Ziel Adresse) mit der zu accountenden IP Adresse übereinstimmen. Ist dies der Fall, wird die entsprechende Datenmenge zu dem Trafficzähler ($$traffic += $acctinfo[6]) summiert. Sollte weder Quell- noch Ziel-IP mit der zu accountenden Adresse übereinstimmen, wird schlicht mit dem nächsten Eintrag der Logdatei weiterverfahren, ohne eine Summierung vorzunehmen. Wurde nun jede Zeile der Logdatei bearbeitet, wird eine Meldung mit der errechneten Trafficmenge auf dem Bildschirm ausgegeben und die Datei wieder geschloßen.
Somit ist es möglich mit einem simplen Perlskript Übersicht über den Trafficanfall im Lan oder Wan zu wahren.
Zugegeben, will man nun mehrere IP Adressen mit accouten, ist es recht umständlich das Programm X-mal mit jeweils einer anderen IP Adresse zu starten. Nun auch diesem Problem kann recht einfach Abhilfe geschaffen werden. Das Beispielprogramm demo5-4.pl ist eine leicht modifizierte Version des Programmes demo5-3.pl. Diese Version verwendet keine einzelne IP-Adresse, sondern eine weitere Datei, welche die zu accountenden IP Adressen enthällt. Der Aufbau der Datei ist dabei recht simpel gehalten. In jeder Zeile dieser Steuerdatei steht eine zu accountende IP-Adresse, welche das Programm nun sequentiell abarbeitet.
Programm: demo5-4.pl #!/usr/bin/perl #################################################### ## ## NetAcct Auswertungsprogramm ## as@Saar.DE ## #Programmnamen erkennen @cmd = split ('/', $0); $aufruf = $argv0 = @cmd[$#cmd]; # Aufrufparameter checken if (@ARGV[1]) { # Wenn Parameter da, Auswertung starten $logdatei = @ARGV[0]; $iplist = @ARGV[1]; # Steuerdatei öffnen open ( IPLIST, "< $iplist") || die "Fehler beim Öffnen von $logdatei" ; while (chop ($ipaddress = <IPLIST>)) { # Logdatei öffnen open ( LOGDAT, "< $logdatei") || die "Fehler beim Öffnen von $logdatei" ; $traffic = 0; while (chop($zeile = <LOGDAT>)) { # Datei zeilenweise durchgehen @acctinfo = split ('\t', $zeile); # Zeile in Array splitten if (($acctinfo[2] eq $ipaddress) || ($acctinfo[4] eq $ipaddress)) { $traffic += $acctinfo[6]; # Traffic accounten wenn IP Adresse stimmt } # End-of-IF } # End-of-LOGDAT-WHILE print "$ipaddress:\t$traffic bytes\n"; close (LOGDAT); # Datei schließen } #End-of-IPLIST-While close (IPLIST) ; } else { # Wenn Parameter fehlen, usage - Meldung ausgeben print "usage: $aufruf <Netacct-Logdatei> <IP-Adr. Listendatei>\n\n"; }Wie Sie bestimmt schon bemerkt haben, wird nun der eigentliche Programmteil des Programmes demo5-3.pl von einer weiteren while-Schleife umgeben. Zuvor wird jedoch die Steuerdatei, welche die zu accountenden IP-Adressen enthällt geöffnet. In der while-Schleife wird nun jeweile eine IP-Adresse aus der Steuerdatei eingelesen, mit welcher der Accountinglauf gestartet wird. Dieser Vorgang wird wiederholt, bis die äußere Schleife ein false liefert, sprich alle IP-Adressen der Steuerdatei abgearbeitet sind.
Sehen wir uns zunächst wieder ein einfaches Beispiel an, in welchem eine Zeile von der Tastatur eingelesen und anschließend in eine Datei test1 geschrieben wird, hier im append-, also anhängenden Modus.
Programm: demo5-5.pl #!/usr/bin/perl print "Eingabezeile: "; chop ($eingabe=<STDIN>); open ( TDAT, ">> test1") || die "Fehler beim Öffnen von test1" ; print TDAT "$eingabe\n"; close (TDAT);Dieses kleine Programm liest nun eine Zeile ein und schreibt diese in die zuvor geöffete Datei.
Natürlich lassen sich mit dieser Möglichkeit auch etwas sinnvollere Programme realisieren. Sehen wir dazu doch mal das nächste Beispielprogramm an, welches einen keinen Textkonverter darstellt und aus normalen ASCII Dateien simple HTML Dateien macht, indem es einen HTML-Kopf um die Datei schreibt und die Umlaute und (einen Teil) der Sonderzeichen, wie beispielsweise das Gößer- und Kleinerzeichen in HTML konforme Schreibweise umwandelt. Dabei bleibt die Orginaldatei (ASCII) erhalten und es wird eine neue Datei mit HTML-Code erzeugt.
Programm: demo5-6.pl #!/usr/bin/perl { print "Originaldatei: "; chop ($indat = <STDIN>); print "Neue Datei: "; chop ($outdat = <STDIN>); print "HTML - Dokumenttitel:"; chop ($titel = <STDIN>); # Quelldatei öffnen open (INDAT,$indat) || die "Fehler beim Öffnen von $indat" ; # Zieldatei öffnen open (OUTDAT,"> $outdat") || die "Fehler beim Öffnen von $outdat"; # HTML Kopf schreiben print OUTDAT "<HTML>\n" ; print OUTDAT "<TITLE>$titel</TITLE>\n<BODY>\n"; while ( <INDAT> ) { # Quelldatei einlesen und via Regexp ändern s/&/&/g; s/>/>/g ; s/</</g ; s/\n/<BR>\n/g ; s/\344/ä/g; s/\366/ö/g; s/\374/ü/g; s/\337/ß/g; s/\304/Ä/g; s/\326/Ö/g; s/\334/Ü/g; print OUTDAT ; } # HTML Fuß schreiben print OUTDAT "<P><HR>\n"; print OUTDAT "converted by ascii2html.pl\n"; print OUTDAT "</BODY>\n</HTML>\n"; close (INDAT); # Quelldatei schliessen close (OUTDAT); # Zieldatei schliessen }Das Programm arbeitet nun mit zwei verschiedenen Dateien in unterschiedlichen Modi. Die Quelldatei wird lediglich zum Lesen geöffnet, während die Zieldatei zum Schreiben geöffnet wird. Auch hier wird die Quelldatei (in diesem Fall die ASCII Datei) zeilenweise eingelesen und mittels regulärer Ausdrücke (vgl. Teil 3 dieser Serie) bearbeitet.
Wie Sie sicher bemerkt haben, wird die eingelesene Zeile innerhalb der while- Schleife keiner Variablen zugewiesen, zumindest nicht sichtbar. In diesem Fall stehten die Informationen in der Sondervariablen $_, auf welche sich auch reguläre Ausdrücke als auch die print() Anweisung beziehen, sofern kein anderer Bezeichner angegeben ist. Da dies hier nicht der Fall ist, habe ich diese Art einmal ganz bewußt zur Demonstration gewählt. Auch wenn es auf den ersten Blick etwas verwirrend wirkt, zeigt dies doch auch wieder die Leisungsfähigkeit von Perl.
Perl ist nun sicher nicht der Inbegriff der übersichtlichen Programmierung, auch wenn ich mich bemühe, meine Beispiele recht übersichtlich zu gestalten, wird man in der Praxis kaum auf solche Sourcen stoßen. So würde das Programm demo5-7.pl denselben Zweck erfüllen, die demo5-6.pl, jedoch ist es deutlich kürzer und chotischer, obwohl es im Grunde genommen die gleichen Anweisungen enthält:
Programm: demo5-7-pl #!/usr/bin/perl $fusszeile = "converted by ascii2html<BR>" ; print "Originaldatei: "; chop ($indat = <STDIN>); print "Neue Datei: "; chop ($outdat = <STDIN>); print "HTML - Dokumenttitel:"; chop ($titel = <STDIN>); open (INDAT,$indat); open (OUTDAT,"> $outdat"); print OUTDAT "<HTML>\n" ; print OUTDAT "<TITLE>$titel</TITLE>\n<BODY>"; while (<INDAT>) { s/&/&/g; s/>/>/g; s/</</g; s/\n/<BR>\n/g; s/\344/ä/g; s/\366/ö/g; s/\374/ü/g; s/\337/ß/g; s/\304/Ä/g; s/\326/Ö/g; s/\334/Ü/g; print OUTDAT ;} print OUTDAT "<P><HR>\n"; print OUTDAT "$fusszeile\n"; print OUTDAT "</BODY></HTML>"; close (INDAT); close (OUTDAT);Ich denke (und hoffe), daß ich Ihnen die Dateibehandlung unter Perl nun etwas näher bringen konnte und ansprechende Beispiele gefunden habe. Im nächsten Teil dieser Serie werden wir uns dann noch anssehen, wie man aus Perl heraus andere (meist System-) Programme ansprechen kann und deren Funktion einfach in eigene Programme integrieren kann.
Der Autor |
Achim Schmidt ist staatlich geprüfter Assistent für Automatisierungs- und Computertechnik (HBFS) und beschäftigt sich bereits seit 6 Jahren intensiv mit dem Internet und seinen Diensten. Mit Linux wurde er erstmalig 1992 beim Aufbau eines Mailboxsystemes direkt konfrontiert. Seit 1995 ist er als Netzwerk- und Systemmanager tätig und beschäftigt sich dabei auch mir der Entwicklung und Realisierung WWW- basierter Onlinesysteme und Internetkopplungen. Zu erreichen ist er unter as@Saar.DE. |
Copyright © 1997 Linux-Magazin Verlag