Toolbox (Linux-Magazin, September 2004)

Genau wie ein Handwerker penibel auf den Inhalt seines Werkzeugkoffers achtet, pflegt jeder Programmierer seine persönliche Sammlung unentbehrlicher Skripts. Da Perl-Werkzeuge öfter mal CPAN-Module benötigen, die auf einer reparaturbedürftigen Maschine nicht unbedingt vorliegen, bringt der heute vorgestellte Werkzeugkasten einfach alles mit.

Perl ist ja bekannt dafür, dass man mit nur ganz wenigen Zeilen sehr viel Arbeit erledigen kann. Heute soll's mal um vier Skripts gehen, die zwar trivial implementiert, aber extrem nützlich sind. So nützlich, dass man sie eigentlich überall dabeihaben sollte.

Leider arbeiten Entwickler aber auch oft auf Maschinen, die nicht die neueste Perl-Distribution fahren oder das ein oder andere CPAN-Modul nicht installiert haben. Zwar lässt sich das alles schnell über's Web und mit einer CPAN-Shell beheben, aber es wäre schön, die gesamte Werkzeugkiste sofort betriebsbereit zu haben, wenn nur ein rudimentäres perl installiert ist.

Zum Glück hat Autrijus Tang mit dem CPAN-Modul PAR eine Möglichkeit gefunden, Applikationen mit allen erforderlichen Modulen in Archive zu verpacken, ähnlich wie das unter Java mit .jar-Dateien funktioniert. Da das Perl Archive Toolkit PAR aber nicht unbedingt auf der Zielmaschine verfügbar ist, um ein Archiv zu entpacken, kann man mit PAR sogar alles enthaltende Executables zusammenbauen, die dann magisch auf der Zielmaschine Perlskripts ablaufen lassen, für die die richtige Umgebung eigentlich fehlt.

Auf geht's, beim Schichtl! Hier sind vier meiner geschätzten Werkzeuge, die später ordnungsgemäß verschnürt werden.

Passwortschnüffler

Ein kleiner Base64-(De)Kodierer zum Beispiel ist immer nützlich, wenn man auf dem Web mit Basic Auth arbeitet. Neulich hatte ich mal das Passwort einer Webseite vergessen, die den Browser diese typische Dialogbox in Abbildung 1 aufpoppen ließ. Der Browser erinnerte sich an meine früheren Eingaben und füllte die Felder aus, allerdings bestand das Passwortfeld nur aus Sternchen. Aber ein flugs zwischen Browser und Server geschalteter Proxy (z.B. [1]) bringt in Abbildung 2 zum Vorschein, dass ein String wie dGVzdDpzZWNyZXQ= durch die Leitung fliegt.

Abbildung 1: Der Browser fragt im Basic-Auth-Dialog nach Username und Passwort.

Abbildung 2: Der Base64-String taucht im Log des Proxies auf.

Was steht dort im Klartext? Das Skript in Listing b64.pl bringt es an den Tag:

    $ b64.pl -d dGVzdDpzZWNyZXQ=
    test:secret

Aha, der Username ist test, das Passwort ist secret. Das überrascht nicht, denn es ist ja bekannt, dass Basic Auth wenig nützt, wenn jemand in der Leitung schnüffelt. Umgekehrt zeigt

    $ b64.pl test:secret
    dGVzdDpzZWNyZXQ=

dass b64.pl auch die Kodierung beherrscht. Andere Anwendungen gibt es viele: Base64-Kodierung kommt immer dann zum Einsatz, wenn es darum geht, binäre Daten in ein anzeigbares Format umzuwandeln.

Listing 1: b64.pl

    01 #!/usr/bin/perl
    02 use warnings;
    03 use strict;
    04 
    05 use Getopt::Std;
    06 use MIME::Base64;
    07 
    08 getopts "d", \my %opts;
    09 
    10 die "usage: $0 [-d] string" unless 
    11     defined $ARGV[0];
    12 
    13 if($opts{d}) {
    14     print decode_base64($ARGV[0]), "\n";
    15 } else {
    16     print encode_base64($ARGV[0]);
    17 }

Prozent-Hex im URL-Wald

Auch in URLs sieht man manchmal codierte Sequenzen. Wenn der Browser per GET-Request einen Parameter an den Server schickt, sieht das unter Umständen so aus:

    http://host.com/cgi/foo?p=a%20b%2Fc

Jedes Sonderzeichen wurde vom Browser ins Format %xx umgewandelt, wobei xx der Hexadezimalwert der ASCII-Nummer des Zeichens ist. Mit dem simplen Skript in Listing urlcode.pl lässt sich die Hex-Codierung leicht dekodieren:

    $ urlcode.pl -d a%20b%2Fc
    a b/c

Umgekehrt lässt sich auch eine Zeichenkette zu Testzwecken URL-kodieren. Der Aufruf von urlcode ohne -d wandelt einen übergebenen String ins URL-Format um:

    $ urlcode.pl "a b/c"
    a%20b%2Fc

Dank des CPAN-Moduls URI::Escape ist das Skript urlcode.pl trivial, es testet lediglich mit Getopt::Std, ob -d auf der Kommandozeile vorliegt und ruft dann entsprechend uri_escape oder uri_unescape aus URI::Escape auf.

Listing 2: urlcode.pl

    01 #!/usr/bin/perl
    02 use warnings;
    03 use strict;
    04 
    05 use Getopt::Std;
    06 use URI::Escape;
    07 
    08 getopts "d", \my %opts;
    09 
    10 die "usage: $0 [-d] string" unless 
    11     defined $ARGV[0];
    12 
    13 if($opts{d}) {
    14     print uri_unescape($ARGV[0]), "\n";
    15 } else {
    16     print uri_escape($ARGV[0]), "\n";
    17 }

Atari-Hexdump

Binäre Dateien pflastern den Bildschirm mit Sonderzeichen voll und hängen manchmal sogar das benutzte Terminal auf, wenn man sie mit cat anzeigt. less ist besser, hilft aber auch nicht viel weiter, da es nicht genau angibt, wie lang bestimmte Byte-Sequenzen sind. Das CPAN-Modul Data::Hexdumper hingegen bietet eine Anzeige, wie ich sie schon vor 15 Jahren auf meinem Atari ST1040 schätzte: Links die Hexcodes in Gruppen von 16 Bytes und rechts die Übersetzung in lesbare Zeichen, falls dies möglich ist. Abbildung 3 zeigt, wie hd.pl seinen eigenen Quellcode als Hexdump anzeigt.

Listing 3: hd.pl

    01 #!/usr/bin/perl
    02 use warnings;
    03 use strict;
    04 
    05 my $data = join '', <>;
    06 use Data::Hexdumper;
    07 my $results = hexdump(
    08     data => $data,
    09     number_format => 'C',
    10 );
    11 print $results;

Listing hd.pl ist nur eine schon peinlich triviale Adaption der Data::Hexdumper-Manualseite -- aber selbst ein so einfaches Werkzeug spart oft enorm Zeit!

Abbildung 3: hd.pl zeigt seinen eigenen Quellcode als Hexdump an.

Perl-Switcheroo

Wer viel mit Perl arbeitet, stets mit zitternden Fingern die neueste Distribution ausprobiert oder nur schnell mal feststellen will, ob ein Modul auch mit Antik-Versionen wie 5.00503 funktioniert, braucht die Möglichkeit, schnell zwischen verschiedenen Installationen hin- und herzuspringen.

Dazu installiert man Perl-Distributionen nicht mehr einfach unter /usr, sondern zum Beispiel in einem Verzeichnis perl-installs im Home-Verzeichnis. perl 5.8.4 konfiguriert man zum Beispiel folgendermaßen:

    ./Configure -D prefix=$HOME/perl-installs/perl-5.8.4 -d

Das anschließende make install wird die Distribution unter dem angegebenen Verzeichnis installieren, das Executable perl wird als $HOME/perl-installs/perl-5.8.4/bin/perl verfügbar sein.

Für's schnelle Hin- und Herspringen wird ein symbolischer Link perl-current im Verzeichnis perl-installs angelegt, um die aktuell genutzte Installation zu bezeichnen:

    cd $HOME/perl-installs
    ln -s perl-5.8.4 perl-current

Anschließend entfernt man /usr/bin/perl und setzt statt dessen einen weiteren symbolischen Link an:

    mv /usr/bin/perl /usr/bin/perl.orig
    ln -s $HOME/perl-installs/perl-current/bin/perl /usr/bin/perl

Dies lässt Skripts, die in der Shebang-Zeile #!/usr/bin/perl stehen haben, auf die aktuell gewählte Perl-Installation zugreifen. Auch andere aus der Distribution genutzte Programme/Skripts wie perldoc sollte man entsprechend umbiegen.

Nach diesen Vorbereitungen kommt das Skript in Listing sp.pl (für switch perl) zum Einsatz: Wie Abbildung 4 zeigt, bietet es eine Auswahl von unter $HOME/perl-installs installierten Distributionen an, lässt den Benutzer eine auswählen und setzt daraufhin den symbolischen Link perl-current entsprechend um. Praktisch!

Listing 4: sp.pl

    01 #!/usr/bin/perl
    02 use strict;
    03 use warnings;
    04 
    05 use File::Basename qw(basename);
    06 
    07 my $PERL_HOME = "$ENV{HOME}/perl-installs";
    08 
    09 my(@versions, $count);
    10 
    11 for (<$PERL_HOME/perl-*>) {
    12     next if -l or ! -d;
    13     push @versions, basename($_);
    14 }
    15 
    16 foreach my $v (@versions) {
    17     print "[", ++$count, "] $v\n";
    18 }
    19 
    20 $| = 1;
    21 print "> ";
    22 my $number = <>;
    23 chomp $number;
    24 
    25 die "Invalid choice" unless 
    26   exists $versions[$number-1];
    27 
    28 unlink("$PERL_HOME/perl-current") or
    29     warn "unlink failed ($!)";
    30 symlink("$PERL_HOME/$versions[$number-1]", 
    31         "$PERL_HOME/perl-current") or
    32     die "symlink failed ($!)";

Abbildung 4: Der perl-Selektor

Werkzeugkiste packen mit PAR

Nun verlangen diese vier vorgestellten Werkzeuge allerdings einige Zusatzmodule auf der Zielmaschine: b64.pl fordert MIME::Base64, urlcode.pl braucht URI::Escape. Beide sind nicht in der Standard-Perl-Distribution enthalten und darum mit einiger Wahrscheinlichkeit nicht auf der Zielmaschine installiert.

Um alle vier heute vorgestellten Skripts einfach zu verpacken genügt es, das mit der PAR-Distribution vom CPAN daherkommende Programm pp aufzurufen:

    pp --output=toolbox.exe b64.pl urlcode.pl hd.pl sp.pl

Dies erzeugt ein Binary toolbox.exe, das alle vier Skripts samt aller erforderlichen Zusatzmodule enthält. Fährt die Zielmaschine dieselbe Plattform (Linux in diesem Fall), installiert sich der Werkzeugkoffer folgendermaßen im Verzeichnis toolbox unterhalb des persönlichen bin-Verzeichnisses:

    mkdir ~/bin/toolbox
    cp toolbox.exe ~/bin/toolbox
    cd ~/bin/toolbox
    for i in b64 urlcode hd sp
    done
        ln -s toolbox $i
    done
    export PATH=$PATH:~/bin/toolbox

Die symbolischen Links unter den neuen Skriptnamen zeigen alle auf toolbox.exe und PAR findet selbst heraus, was gemeint ist, wenn der Benutzer jetzt zum Beispiel b64 aufruft. Es wird b64.pl aus dem Archiv extrahieren, die von ihm geforderten Zusatzmodule ebenfalls aus dem Archiv laden und das Skript ohne Murren ausführen.

PAR unterstützt sogar mehrere Plattformen gleichzeitig, man muss die Werkzeuge nur mit der Option --multiarch unter den verschiedenen Betriebssystemen nacheinander in die Kiste packen. Das Tutorial, das PAR als PAR::Tutorial beiliegt, zeigt, wie's im Einzelnen geht.

Eines ist zu beachten: man sollte den Koffer auf einer möglichst alten Installation packen, sonst kann es sein, dass es auf einer neuen Zielmaschine Probleme mit einer älteren libc gibt.

Packt eure Werkzeugkoffer!

Infos

[1]
Michael Schilli, ``Loggender Proxy'', Linux-Magazin 04/2000, http://www.linux-magazin.de/Artikel/ausgabe/2000/04/Proxy/proxy.html


Author

Michael Schilli

arbeitet als Web-Engineer für AOL/Netscape in Mountain View, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com.