---
title: Perl Einzeiler
tags:
- IT/Development/Perl
---
# Hilfreiche Perl-Einzeiler für Unix und Windows
Michael Schilli
## Aufmacher
Statt unleserliche Wunderwerke zu erzeugen - wie im regelmäßig stattfindenden Obfuscated Perl Contest - kann man mit Perl-Einzeilern auch flott Alltagsprogrammierjobs erledigen. Es folgt eine Sammlung der wichtigsten, die man immer im Handgepäck haben sollte.
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
```shell
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.
grep, awk und sed in einem
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:
```shell
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:
```perl
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
```shell
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:
```shell
perl -l -n -a -F: -e '$high = $F[2] if $F[2] > $high;
END {print $high+1}' /etc/passwd
```
Zeile 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.
## Dateien automatisch editieren
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:
```shell
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
```shell
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:
```shell
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:
```shell
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 http://www.aol.com/netfind wird so flugs http://www.aol.com/netfind/, 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
```shell
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
```shell
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.
## Kopieren und Verschieben à la Unix
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:
```shell
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)
```shell
perl -MFile::Copy -e 'for() \
{move($_, "test.old") if -M > 7}'
```
ins Verzeichnis test.old. Alles, was dort älter als drei Wochen ist, löscht
```shell
perl -e 'for() {unlink $_ if -M > 21}'
```
kommentarlos.
## Uuencode und Base 64 entziffern
Für schnelles Dekodieren von uuencode- und Base64-kodierter Information stehen in Perl die unpack-Funktion sowie das Modul MIME::Base64 zur Verfügung.
```shell
perl -e 'print unpack("u", join("", <>))' data.uu
```
entpackt 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
```shell
perl -MMIME::Base64 -e 'print MIME::Base64::decode(join("", <>))' datafile
```
gibt 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'.