
Perl-Skripts müssen nicht in einer Datei stehen. Der Interpreter verarbeitet auch mit der Option -e hereingereichte Strings. Um zum Beispiel 61 000 DM Jahresgehalt mal schnell durch 12 zu teilen, reicht die Eingabe
perl -l -e 'print 61000 / 12'
und Perl rechnet fließkommagenau 5083.33333333333 aus. Zu beachten ist bei solchen Aufrufen von der Kommandozeile, daß der auszuführende Code in Unix-Shells in einfachen Anführungszeichen steht (sonst expandiert die Shell Sonderzeichen wie * und $), der Command-Interpreter unter Windows 95 und NT jedoch doppelte Anführungszeichen verlangt. Die vorangestellte Option -l dient hier nur dazu, hinter das Ergebnis ein Newline-Zeichen einzufügen.
Perls Stärke ist zweifellos die Manipulation von Texten, und so steht mit -n eine Option zur Verfügung, mit der sich auf der Kommandozeile angegebene (oder durch die Standardeingabe hereinkommende) Dateien bearbeiten lassen:
perl -n -e 'print $_ if /mschilli/' /etc/passwd cat /etc/passwd | perl -n -e "print $_ if /mschilli/"
Beide Befehle sehen nach, ob in der Unix-Paßwortdatei irgendwo der String 'mschilli' vorkommt, und geben passende Zeilen aus. Intern wickelt der Perl-Interpreter bei gesetzter Option -n nämlich eine while-Schleife um den angegebenen Kommandostring, die entweder auf der Kommandozeile angegebene Dateien oder den Datenstrom der Standardeingabe zeilenweise abarbeitet:
while (<>) { print $_ if /mschilli/; }
Da die Paßwortdatei bekanntlich Einträge vom Format
mschilli:8KYD6mggn4tsI:501:100:Michael Schilli:/home/mschilli:/bin/bash
enthält, wäre es angebracht, die durch Doppelpunkte getrennten Einzelfelder gesondert zu untersuchen. Bei gesetzter Option -a zerlegt Perl die Felder der hereinkommenden Zeilen in die Elemente eines Array mit dem Spezialnamen @F, wobei es den in der -F-Option gesetzten Feldtrenner benutzt, oder, falls diese fehlt, an Leerzeichen oder Tabulatoren trennt. Die Benutzerkürzel, also die ersten Felder der Paßwortdateieinträge spuckt somit die Konstruktion
perl -l -a -F: -n -e 'print $F[0]' /etc/passwd
aus. Daraus läßt sich eine hilfreiche Anwendung ableiten: Das dritte Feld des Paßworteintrags enthält die im System eindeutige Benutzernummer. Möchte der Administrator einen Account für einen neuen Benutzer einrichten, sucht er - falls er, wie alle 'echten' Sysadmins, ohne GUI-Schnickschnack arbeitet - nach der nächsten, noch unbenutzten Nummer. Da Perl wie der gute alte awk ein END-Konstrukt bietet, dessen Inhalt der Interpreter nach der impliziten -n-Schleife ausführt, findet folgendes Konstrukt die gesuchte Nummer:
perl -l -n -a -F: -e '$high = $F[2] if $F[2] > $high;
END {print $high+1}' /etc/passwdZeile für Zeile durchsucht dieses Skript die Paßwortdatei und setzt $high gleich der aktuellen Benutzernummer im dritten Feld, falls diese größer als der bislang gespeicherte Wert ist. So steht am Ende der Datei die höchste vergebene Benutzernummer in $high. Anschließend kommt der Code im END-Konstrukt zur Ausführung und gibt eine um eins größere Zahl aus.
Damit Perl bei solchen impliziten Schleifen die jeweils bearbeitete Zeile automatisch ausgibt, muß statt -n die Option -p stehen. Dies hilft beim Suchen und Ersetzen von Textstücken:
perl -p -e 's/a/b/g' datei
ersetzt in datei alle 'a' durch 'b' und gibt den modifizierten Inhalt auf der Standardausgabe aus. Um den Inhalt der Datei selbst zu verändern, muß die -i-Option (für in-place-edit) herhalten: Der Aufruf
perl -p -i.bak -e 's/a/b/g' datei
sichert zunächst für alle Fälle datei nach datei.bak und führt anschließend die Zeichenersetzung in datei selbst durch. Das obige Kommando funktioniert auch für mehrere Dateien gleichzeitig:
perl -p -i.bak -e 's/\bprintf\b/myprintf/g' *.c
ersetzt unter Unix, wo die Shell *.c zu einer Reihe von Dateinamen expandiert, in allen C-Dateien im gegenwärtigen Verzeichnis sämtliche Aufrufe von printf (Wortgrenzen links und rechts) durch eine neue Funktion myprintf und sichert gleichzeitig die alten Dateien nach *.c.bak. Kommt ein Kurzskript nicht mit dem Funktionsumfang der Standard-Perl-Bibliothek aus, sondern benötigt Zusatzmodule, veranlaßt die Option -M den Interpreter, die angegebenen Module einzubinden. Somit steht Einzeilern die Welt offen: Das praktische Modul LWP::Simple aus der Programmsammlung libwww beispielweise gewährt einfachen Zugriff auf das World Wide Web:
perl -MLWP::Simple -e 'getprint("http://www.aol.com")' holt den Inhalt der Web-Seite http://www.aol.com vom Netz und gibt ihn auf der Standardausgabe aus. Zu beachten ist jedoch, daß LWP::Simple nur einfache Web-Zugriffe beherrscht, Redirects sind nicht im Funktionsumfang enthalten. Verweist ein angegebener URL statt auf eine Datei auf ein Verzeichnis auf dem Zielrechner, vollführen moderne Browser einen Redirect, aus <A HREF="http://www.aol.com/">http://www.aol.com/netfind</A> wird so flugs <A HREF="http://www.aol.com/netfind/">http://www.aol.com/netfind/</A>, und nur der zweite URL ist für LWP::Simple geeignet.
Um den Inhalt einer Web-Seite nicht nur auszugeben, sondern gleich wegzuschreiben, hilft das Konstrukt
perl -MLWP::Simple -e \
'getstore("http://www.aol.com", "aol.html")'Es speichert die geholte Information gleich in der Datei aol.html auf der lokalen Festplatte. Schon kein richtiger Einzeiler mehr, aber dennoch ganz praktisch ist
cat aol.html | \
perl -MHTML::TreeBuilder -e \
'print map {"$_->[0]\n" }\
@{HTML::TreeBuilder->new->parse_file(\*STDIN)->\
extract_links}' Es extrahiert alle Hyperlinks aus dem per cat weitergereichten HTML-Dokument. Wie? Nun, das ist etwas komplizierter: Das erzeugte Objekt vom Typ HTML::TreeBuilder ruft seine parse_file-Methode mit einer Glob-Referenz des Standardeingabedeskriptors (\*STDIN) auf. Dadurch erstellt es ein HTML::Parser-Objekt, dessen Methode extract_links eine Referenz auf ein Array zurückgibt. Dieses besteht wiederum aus Array-Referenzen, die als erstes Element den Linknamen enthalten. Die Funktion map nimmt einen Codeblock und eine Liste entgegen, ruft für jedes Element der Liste den Codeblock auf (wobei der Wert des aktuell bearbeiteten Elements in der Spezialvariablen $_ liegt) und setzt dessen Rückgabewert in die Ergebnisliste ein. Nachdem das Skript also die von extract_links gelieferte Array-Referenz mit dem Konstrukt @{...} in ein Array verwandelt hat, steht im Codeblock mit $_ eine Referenz auf das Unter-Array zur Verfügung. $_->[0] extrahiert dessen erstes Element.
File::Copy schließlich erlaubt das Verschieben und Kopieren von Dateien im Unix-Look&Feel: Die Funktionen move und copy nehmen als zweiten Parameter auch ein Verzeichnis, in das sie die als ersten Parameter spezifizierte Datei verschieben beziehungsweise kopieren. Windows-Benutzer werden folgendes zu schätzen wissen:
perl -MFile::Copy -e 'for(<*.c>) {copy $_, "/backups"}'
kopiert alle *.c-Dateien im gegenwärtigen Verzeichnis ins Verzeichnis /backups, falls dieses existiert. Die for-Schleife iteriert mit dem File-Globbing-Konstrukt über alle *.c-Dateien und setzt für den Schleifenrumpf $_ auf die aktuelle Datei. Da die Windows-Version des Perl-Interpreters dankenswerterweise auch Unix-Pfadnamen versteht, entspricht /backups in Perl dem Windows-Pfad \BACKUPS, im Normalfall also C:\BACKUPS.
Alle Dateien im Verzeichnis test, die seit einer Woche nicht modifiziert wurden, verschiebt (diesmal wieder in der Schreibweise für Unix-Shells)
perl -MFile::Copy -e 'for(<test/*>) \ {move($_, "test.old") if -M>7}'
ins Verzeichnis test.old. Alles, was dort älter als drei Wochen ist, löscht
perl -e 'for(<test.old/*>) {unlink $_ if -M>21}'
kommentarlos.
Für schnelles Dekodieren von uuencode- und Base64-kodierter Information stehen in Perl die unpack-Funktion sowie das Modul MIME::Base64 zur Verfügung.
perl -e 'print unpack("u", join("", <>))' data.uuentpackt die kodierte Information aus der Datei data.uu und leitet sie auf die Standardausgabe. Zu beachten ist jedoch, daß unpack nur die eigentlichen kodierten Daten verarbeitet, aus einer typischen UU-Datei, die zusätzlich noch den Namen der kodierten Datei und ein Ende-Tag im Format
begin 644 data )2&D*:&D*:&D* ` end
enthält, sind vor der Bearbeitung die erste sowie die letzte Zeile zu entfernen. Die besonders in EMail verwendete Base64-Kodierung knackt das Zusatzmodul MIME::Base64. Der Einzeiler
perl -MMIME::Base64 -e \
'print MIME::Base64::decode(join("", <>))' datafilegibt die dekodierten Base64-Daten in datafile auf die Standardausgabe aus, wobei - analog zur UU-Kodierung - darauf zu achten ist, daß in datafile nur der Base64-Buchstabensalat liegt, MIME-Header und die einleitende Sequenz, die unter anderem den Dateinamen festlegt, müssen vorher manuell entfernt werden.
Genug der Beispiele. Wer noch tiefer in die Syntax von Perls Kommandozeilenoptionen einsteigen will, dem sei die Manual-Seite empfohlen, die auf das Kommando perldoc perlrun zum Vorschein kommt.
Michael Schilli
arbeitet als Web-Engineer für America Online, Inc., San Mateo. Er ist Autor des im Juni bei Addison-Wesley erscheinenden Buches 'GoTo Perl 5'.
Dieser Text ist der Zeitschriften-Ausgabe 07/1998 von iX entnommen.
Parallelprogrammierung - die Kunst der Multi-Core-Nutzung
Agile ALM - agile Praktiken im Application Lifecycle Management
Webentwicklung - Applikationen für mobile Clients
HTML5, CSS3, WebGL: Das iX-Sonderheft zum Thema Webdesign fasst die wichtigsten Neuerungen der aktuellen und kommenden Webstandards zusammen.