Juni 2008 Archive

Gabor Szabo (Perl Training Israel), der seit mehren Jahren Trainings zum Thema Testing und Qualitätssicherung hält, veröffentlicht seit kurzem in unregelmäßigen Abständen Test Automation Tips per E-Mail.

Der erste Beitrag Testing Hello World zeigt, das Gabor das Thema von Grund auf angeht.

Lesenswert.

Siehe auch

Bei einer URL- oder URI-Kodierung geht es darum, bestimmte Zeichen in einer URL (URI) durch eine oder mehrere Gruppen aus speziellen anderen Zeichen zu ersetzen.

URI::Escape von Gisle Aas erleichtert das Kodieren bzw. Dekodieren von URIs erheblich und bietet neben UTF8-Unterstützung auch Sicherheitsfeatures.

Beispiel: URI kodieren und dekodieren

Die beiden Subroutinen uri_escape() und uri_unescape(), die automatisch von URI::Escape exportiert werden, übernehmen die Arbeit.

#!/usr/bin/perl
use warnings;
use strict;
use URI::Escape;

my $klartext = qq~"Dies ist Klartext mit Sonderzeichen!"  10% mehr \$ ... ~;
my $encoded  = uri_escape($klartext);
my $decoded  = uri_unescape($encoded);

print "Encoded: $encoded \n";
print "Decoded: $decoded \n";

Das Programm erzeugt folgende Ausgabe:

Encoded: %22Dies%20ist%20Klartext%20mit%20Sonderzeichen!%22%20%2010%25%20mehr%20%24%20...%20 
Decoded: "Dies ist Klartext mit Sonderzeichen!"  10% mehr $ ...  

UTF8

uri_escape_utf8() kann UTF8 Strings kodieren, uri_unescape() kann diese ebenfalls dekodieren.

CGI-Formulare

Das Modul CGI kodiert bzw. dekodiert Formulardaten automatisch richtig. Verwenden Sie daher CGI und nicht URI::Escape.

Beispiel: GET-Query mit URI::Escape zusammensetzen

#!/usr/bin/perl
use strict;
use warnings;

use URI::Escape;

# Basis URI zum Formular

my $url = 'http://irgendwo.tld/cgi-bin/formular.pl';

# Parameter und Werte
my $param1 = 'vorname';
my $param2 = 'nachname';

my $value1 = 'Max';
my $value2 = 'Müller';

# Get Query schrittweise zusammenbauen

my $get_query = '';
$get_query .= $url;
$get_query .= '?';

$param1 = uri_escape($param1);
$get_query .= $param1;
$get_query .= '=';

$value1 = uri_escape($value1);
$get_query .= $value1;

$get_query .= '&';

$param2 = uri_escape($param2);
$get_query .= $param2;
$get_query .= '=';
$value2 = uri_escape($value2);
$get_query .= $value2;


print "GET Query: $get_query\n";

Das Programm erzeugt folgende Ausgabe:

GET Query: http://irgendwo.tld/cgi-bin/formular.pl?vorname=Max&nachname=M%C3%BCller

Beispiel: GET-Query mit CGI zusammensetzen

Zum Vergleich die selben Schritte wie oben, jedoch mit CGI.

#!/usr/bin/perl
use strict;
use warnings;

use CGI;

my $q = new CGI;


# Basis URI zum Formular

my $url = 'http://irgendwo.tld/cgi-bin/formular.pl';

# Parameter und Werte
my $param1 = 'vorname';
my $param2 = 'nachname';

my $value1 = 'Max';
my $value2 = 'Müller';

# Get Query schrittweise zusammenbauen

my $get_query = '';
$get_query .= $url;
$get_query .= '?';

$param1 = $q->escape($param1);
$get_query .= $param1;
$get_query .= '=';

$value1 = $q->escape($value1);
$get_query .= $value1;

$get_query .= '&';

$param2 = $q->escape($param2);
$get_query .= $param2;
$get_query .= '=';
$value2 = $q->escape($value2);
$get_query .= $value2;


print "GET Query: $get_query\n";

Das Programm erzeugt die selbe Ausgabe wie oben:

GET Query: http://irgendwo.tld/cgi-bin/formular.pl?vorname=Max&nachname=M%C3%BCller

Tabellarische Übersicht URL Escape Codes

Zeichen Escape Code
Space %20
" %22
! %23
. %2E
, %2C
< %3C
> %3E
# %23
% %25
( %28
) %29
{ %7B
} %7D
| %7C
\ %74
^ %5E
~ %7E
[ %5B
] %5D
' %60
; %3B
/ %2F
? %3F
: %3A
@ %40
= %3D
& %26
* %2A

 

Siehe auch

Fall 1: Kleine Dateien

Falls die Datei klein ist, kann man sie einfach komplett in ein Array einlesen und dieses mit reverse() umdrehen.

#!/usr/bin/perl
use strict;
use warnings;

my $file = 'test.txt';

my @zeilen_rueckwaerts = ();

open (FH, '<', $file) or die "Fehler open()  $!";
@zeilen_rueckwaerts = reverse <FH>;
close(FH) or die "Fehler close() $!";

my $zeile = '';
foreach $zeile (@zeilen_rueckwaerts) {
          # Zeilenenden wie in der eingelesenen Datei
        print $zeile;
}

Angewandt auf die Beispieldatei test.txt

Zeile 01
Zeile 02
Zeile 03
Zeile 04
Zeile 05

ergibt sich folgende Ausgabe:

Zeile 05
Zeile 04
Zeile 03
Zeile 02
Zeile 01

Fall 2: Große Dateien

Das CPAN-Modul File::ReadBackwards kann auch mit sehr großen Dateien umgehen. Die Datei muss nicht in den Hauptspeicher passen.

#!/usr/bin/perl
use warnings;
use strict;

use File::ReadBackwards ;

my $file = 'test.txt';

my $bw = File::ReadBackwards->new( $file ) or
                        die "can't read $file $!" ;

my $line = '';

while( defined( $line = $bw->readline ) ) {
                # Zeilenenden wie in der eingelesenen Datei
               print $line ;
}
exit();

File::ReadBackwards ist nicht gedacht, um Änderungen in einer Datei zu verfolgen, dafür gibt es File::Tail.

Rückwärts suchen

Das nachfolgende Beispiel durchsucht eine Datei rückwärts, d.h. die Suche beginnt in der letzten Zeile, und stoppt sobald der erste Treffer erzielt wurde. Das ist sehr praktisch, wenn man in einer Log-Datei nach dem neuesten Ereignis suchen möchte.

#!/usr/bin/perl
use warnings;
use strict;

use File::ReadBackwards ;

my $file = 'test.txt';

my $bw = File::ReadBackwards->new( $file ) or
                        die "can't read $file $!" ;

my $line = '';

my $suchbegriff = '03';

while( defined( $line = $bw->readline ) ) {
        print "Bearbeite $line";
        if ( $line =~ /$suchbegriff/) {
                print "Treffer: $line" ;
                        # Suche beenden
                last;
        }
}

Das Programm erzeugt folgende Ausgabe

Bearbeite Zeile 05
Bearbeite Zeile 04
Bearbeite Zeile 03
Treffer: Zeile 03

Caveats

File::ReadBackwards (Version 1.04) unterstützt flock() nicht. Es kann also vorkommen, daß die letzte Zeile nicht komplett zurückgegeben wird. Dies hängt auch von der Größe des Dateipuffers des Betriebssystems und der Anzahl der Prozesse, die gerade in die Datei schreiben, ab.

Siehe auch

Smarte Datenstrukturen und dummer Code funktionieren viel besser als umgekehrt

Brooks, Kapitel 9: "Also zeige mir Deinen [Code], aber verhülle Deine [Datenstrukturen], und ich werde auf ewig im Dunkeln tappen. Zeige mir aber Deine [Datenstrukturen] und ich werde Deinen [Code] gar nicht brauchen, denn ich weiß, wie er aussieht." (Quelle: Die Kathedrale und der Basar.)

Smarte Datenstrukturen sind manchmal auch komplex. Hier hilft das Standardmodul Dumpvalue, das die übersichtliche Darstellung komplexer Datenstrukturen unterstützt. Die für diesen Zweck häufig verwendeten CPAN-Module Data::Dumper und Data::Dump::Streamer sind eigentlich zur Serialisierung von Datenstrukturen entwickelt worden und weniger zur Darstellung.

Dieser Beitrag geht auf folgende häufig anzutreffende Datenstrukturen ein

  • Arrays of Arrays
  • Hashes of Arrays
  • Arrays of Hashes
  • Hashes of Hashes
  • Hash mit komplexer Datenstruktur

und zeigt wie Dumpvalue diese darstellt.

dumpvalue()

Die Methode dumpvalue() aus dem Paket Dumpvalue, erwartet als Parameter eine Referenz (\) auf eine Datenstruktur und gibt eine Darstellung der Datenstruktur im Listenkontext zurück.

Gibt man dumpvalue() jedoch eine Referenz auf eine Referenz (\\) auf eine Datenstruktur als Parameter, so wird die Ausgabe wesentlich informativer. In diesem Fall wird das Ausgabeformat des Perl-Debuggers verwendet.

Im Abschnitt Arrays of Arrays werden beide Formen kurz dargestellt. In den weiteren Abschnitten wird durchgehend die zweite Form (\\) verwendet.

Arrays of Arrays

#!/usr/bin/perl
use strict;
use warnings;
use Dumpvalue;
my $dumper = new Dumpvalue;

my @AoA = (
        [ "fred", "barney" ],
        [ "george", "jane", "elroy" ],
        [ "homer", "marge", "bart" ],
      );

$dumper->dumpValue(\@AoA);

Das Programm erzeugt folgende Ausgabe:

0  ARRAY(0x8153c28)
   0  'fred'
   1  'barney'
1  ARRAY(0x8190814)
   0  'george'
   1  'jane'
   2  'elroy'
2  ARRAY(0x81953f0)
   0  'homer'
   1  'marge'
   2  'bart'

Verwendet man zwei Backslashes (\\), werden mehr Details angezeigt.

$dumper->dumpValue(\\@AoA);

Das Programm erzeugt jetzt folgende Ausgabe:

-> ARRAY(0x8154630)
   0  ARRAY(0x8153c28)
      0  'fred'
      1  'barney'
   1  ARRAY(0x8190814)
      0  'george'
      1  'jane'
      2  'elroy'
   2  ARRAY(0x81953f0)
      0  'homer'
      1  'marge'
      2  'bart'

Hashes of Arrays

#!/usr/bin/perl
use strict;
use warnings;
use Dumpvalue;
my $dumper = new Dumpvalue;

my  %HoA = (
        flintstones        => [ "fred", "barney" ],
        jetsons            => [ "george", "jane", "elroy" ],
        simpsons           => [ "homer", "marge", "bart" ],
      );
$dumper->dumpValue(\\%HoA);

Das Programm erzeugt folgende Ausgabe:

-> HASH(0x8154630)
   'flintstones' => ARRAY(0x8153c28)
      0  'fred'
      1  'barney'
   'jetsons' => ARRAY(0x8190880)
      0  'george'
      1  'jane'
      2  'elroy'
   'simpsons' => ARRAY(0x819545c)
      0  'homer'
      1  'marge'
      2  'bart'

Arrays of Hashes

#!/usr/bin/perl
use strict;
use warnings;
use Dumpvalue;
my $dumper = new Dumpvalue;

my @AoH = (
        {
            Lead     => "fred",
            Friend   => "barney",
        },
        {
            Lead     => "george",
            Wife     => "jane",
            Son      => "elroy",
        },
        {
            Lead     => "homer",
            Wife     => "marge",
            Son      => "bart",
        },
  );
$dumper->dumpValue(\\@AoH);

Das Programm erzeugt folgende Ausgabe:

-> ARRAY(0x8154630)
   0  HASH(0x8153c28)
      'Friend' => 'barney'
      'Lead' => 'fred'
   1  HASH(0x81954ec)
      'Lead' => 'george'
      'Son' => 'elroy'
      'Wife' => 'jane'
   2  HASH(0x8195534)
      'Lead' => 'homer'
      'Son' => 'bart'
      'Wife' => 'marge'

Hashes of Hashes

#!/usr/bin/perl
use strict;
use warnings;
use Dumpvalue;
my $dumper = new Dumpvalue;

my %HoH = (
        flintstones => {
                lead      => "fred",
                pal       => "barney",
        },
        jetsons     => {
                lead      => "george",
                wife      => "jane",
                "his boy" => "elroy",
        },
        simpsons    => {
                lead      => "homer",
                wife      => "marge",
                kid       => "bart",
        },
 );
$dumper->dumpValue(\\%HoH);

Das Programm erzeugt folgende Ausgabe:

-> HASH(0x8154630)
   'flintstones' => HASH(0x8153c28)
      'lead' => 'fred'
      'pal' => 'barney'
   'jetsons' => HASH(0x81954fc)
      'his boy' => 'elroy'
      'lead' => 'george'
      'wife' => 'jane'
   'simpsons' => HASH(0x819552c)
      'kid' => 'bart'
      'lead' => 'homer'
      'wife' => 'marge'

Hash mit komplexer Datenstruktur

#!/usr/bin/perl
use strict;
use warnings;
use Dumpvalue;
my $dumper = new Dumpvalue;

my      %TV = (
        flintstones => {
            series   => "flintstones",
            nights   => [ qw(monday thursday friday) ],
            members  => [
                { name => "fred",    role => "lead", age  => 36, },
                { name => "wilma",   role => "wife", age  => 31, },
                { name => "pebbles", role => "kid",  age  =>  4, },
            ],
        },

        jetsons     => {
            series   => "jetsons",
            nights   => [ qw(wednesday saturday) ],
            members  => [
                { name => "george",  role => "lead", age  => 41, },
                { name => "jane",    role => "wife", age  => 39, },
                { name => "elroy",   role => "kid",  age  =>  9, },
            ],
         },

        simpsons    => {
            series   => "simpsons",
            nights   => [ qw(monday) ],
            members  => [
                { name => "homer", role => "lead", age  => 34, },
                { name => "marge", role => "wife", age => 37, },
                { name => "bart",  role => "kid",  age  =>  11, },
            ],
         },
      );
$dumper->dumpValue(\\%TV);

Das Programm erzeugt folgende Ausgabe:

-> HASH(0x8154630)
   'flintstones' => HASH(0x81d49d4)
      'members' => ARRAY(0x81d4998)
         0  HASH(0x81d4824)
            'age' => 36
            'name' => 'fred'
            'role' => 'lead'
         1  HASH(0x81d4860)
            'age' => 31
            'name' => 'wilma'
            'role' => 'wife'
         2  HASH(0x81d495c)
            'age' => 4
            'name' => 'pebbles'
            'role' => 'kid'
      'nights' => ARRAY(0x8153c28)
         0  'monday'
         1  'thursday'
         2  'friday'
      'series' => 'flintstones'
   'jetsons' => HASH(0x81d4b30)
      'members' => ARRAY(0x81d4af4)
         0  HASH(0x81d4a40)
            'age' => 41
            'name' => 'george'
            'role' => 'lead'
         1  HASH(0x81d4a7c)
            'age' => 39
            'name' => 'jane'
            'role' => 'wife'
         2  HASH(0x81d4ab8)
            'age' => 9
            'name' => 'elroy'
            'role' => 'kid'
      'nights' => ARRAY(0x81d4a10)
         0  'wednesday'
         1  'saturday'
      'series' => 'jetsons'
   'simpsons' => HASH(0x81d4c80)
      'members' => ARRAY(0x81d4c44)
         0  HASH(0x81d4b90)
            'age' => 34
            'name' => 'homer'
            'role' => 'lead'
         1  HASH(0x81d4bcc)
            'age' => 37
            'name' => 'marge'
            'role' => 'wife'
         2  HASH(0x81d4c08)
            'age' => 11
            'name' => 'bart'
            'role' => 'kid'
      'nights' => ARRAY(0x81d4b6c)
         0  'monday'
      'series' => 'simpsons'

Siehe auch

Wenn Sie Ihre Perl-Programme unter Linux entwickeln und häufig die Kommandozeile nutzen, lohnt es sich ein paar Minuten in die Automatisierung vieler Aufgaben zu investieren.

Hier bietet sich die Nutzung von Shell-Aliasen an, die permanent in einer Datei (z.B. ~/.perlaliases) gespeichert und bei jedem Login automatisch zur Verfügung gestellt werden.

Zahlenbasis konvertieren (Base Conversion)

Die folgenden Aliase sind bei der Umrechnung von einem Zahlensystem in ein anderes recht nützlich.

alias d2h="perl -e 'printf qq|%X\n|, int( shift )'"
alias d2o="perl -e 'printf qq|%o\n|, int( shift )'"
alias d2b="perl -e 'printf qq|%b\n|, int( shift )'"

alias h2d="perl -e 'printf qq|%d\n|, hex( shift )'"
alias h2o="perl -e 'printf qq|%o\n|, hex( shift )'"
alias h2b="perl -e 'printf qq|%b\n|, hex( shift )'"

alias o2h="perl -e 'printf qq|%X\n|, oct( shift )'"
alias o2d="perl -e 'printf qq|%d\n|, oct( shift )'"
alias o2b="perl -e 'printf qq|%b\n|, oct( shift )'"

Quelle: Journal of brian_d_foy : Base conversions

Modulversionen ermitteln

Um die Version eines installierten Moduls muss man jedes Mal etwas wie

        perl -MName::des::Moduls -le "print Name::des::Moduls->VERSION"

eingeben. Das lässt sich recht einfach verkürzen.

function pmver ( ) {
        # Gibt die Version des (installierten) Perlmoduls aus
        # Usage pmver Perlmodul
        # Beispiel pmver CGI
        perl -M$1 -le "print $1->VERSION"
}

Beispiel

$ pmver CGI

3.15

Quelle: Hack #4 in Perl Hacks (ISBN: 3897214741)

Perl Debugger

Den Perl Debugger verwende ich häufig (perl -d programm.pl args ) auch als Perl-Shell (perl -d -e 0). Die Funktion debug erspart mir Tipparbeit.

function debug () {
        if [ -z $1 ]
        then
                perl -d -e 0
        else
                perl -d $@
        fi
}

Wenn debug ohne Parameter aufgerufen wird, wird eine Perl-Shell geöffnet; beim Aufruf mit Parametern werden die Parameter einfach an den Debugger weitergereicht.

$ debug

Loading DB routines from perl5db.pl version 1.28
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):	0
  DB<1> 

$ debug HalloWelt.pl

Loading DB routines from perl5db.pl version 1.28
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(HalloWelt.pl:4):	print "Hallo Welt\n";

Verschiedene Perlversionen

Häufig habe ich mehrere Perlversionen auf meinem Rechner. Für jede Perlversion, die nicht im Standardpfad ($PATH) aufgeführt ist, lege ich einen eigenen Alias an.

alias perl510="/opt/perl510/bin/perl/"

Beispiel

$ perl -v 

This is perl, v5.8.8 built for i486-linux-gnu-thread-multi
...
$ perl510 -v 

This is perl, v5.10.0 built for i686-linux
...

Unterschiedliche Modulversionen

Manchmal muss ich meinen Code mit unterschiedlichen Modulversionen testen, z.B. nach Updates eines CPAN-Moduls. Das neue Modul wird dazu händisch in einen eigenen Ordner (z.B. /opt/devel/) installiert. Hier hilft der Schalter -I, der den @INC anpasst.

alias newmods="/2008/06/perl--I-/opt/devel/"

Beispiel (inc.pl)

#!/usr/bin/perl
use warnings;
use strict;
print join("\n", @INC);

Wenn das Programm wie gewohnt aufgerufen wird, ergibt sich folgende Ausgabe.

$ perl inc.pl
/etc/perl
/usr/local/lib/perl/5.8.8
....

Bei der Verwendung von newmods werden die neueren Module aus /opt/devel zuerst geladen.

$  newmods inc.pl 
/opt/devel/
/etc/perl
/usr/local/lib/perl/5.8.8
....

Alle aufgeführten Aliase in der Übersicht

Hier noch einmal alle angesprochenen Aliase und Funktionen in einer Übersicht.

alias d2h="perl -e 'printf qq|%X\n|, int( shift )'"
alias d2o="perl -e 'printf qq|%o\n|, int( shift )'"
alias d2b="perl -e 'printf qq|%b\n|, int( shift )'"

alias h2d="perl -e 'printf qq|%d\n|, hex( shift )'"
alias h2o="perl -e 'printf qq|%o\n|, hex( shift )'"
alias h2b="perl -e 'printf qq|%b\n|, hex( shift )'"

alias o2h="perl -e 'printf qq|%X\n|, oct( shift )'"
alias o2d="perl -e 'printf qq|%d\n|, oct( shift )'"
alias o2b="perl -e 'printf qq|%b\n|, oct( shift )'"

alias perl510="/opt/perl510/bin/perl/"

alias newmods="/2008/06/perl--I-/opt/devel/"

function pmver ( ) {
        # Gibt die Version des (installierten) Perlmoduls aus
        # Usage pmver Perlmodul
        # Beispiel pmver CGI
        perl -M$1 -le "print $1->VERSION"
}

function debug () {
       echo "Parameter $1"
        if [ -z $1 ]
        then
                perl -d -e 0
        else
                perl -d $@
        fi
}

Vorschläge bitte

Weitere produktive Shell-Aliase bitte in die Kommentare. Danke.

Siehe auch

Nach einer knappen Übersicht der POP3-Kommandos gemäß RFC 1225 führt Sie diese Seite durch eine Telnet-Beispielsitzung mit einem POP3-Server, die Ihnen zeigt, wie Anfragen und Antworten zwischen Client und Server ablaufen.

POP3 Kommandos (RFC 1225)

APOP
Verschlüsseltes Einloggen (Optional)
DELE
Markiert eine Nachricht als gelöscht.
LAST
Gibt die höchste bisher bearbeitete Nachrichtennummer zurück.
LIST
Gibt die Größe der Nachricht(en) zurück.
NOOP
No Operation, gibt einen positiven Wert zurück, falls der Server noch lebt.
PASS
Übermittelt das Passwort für USER im Klartext
RSET
Setzt die Markierung aller als gelöscht markierten Nachrichten zurück.
RETR
Holt eine komplette Nachricht (Head und Body).
STAT
Ermittelt die Anzahl der vorhandenen Nachrichten und die Größe der Mailbox.
TOP
Holt den Header und die angegebenen Zeilen der Nachricht.
TOP 10 5 holt den Header und die ersten 5 Zeilen von Nachricht 10.
(Optional)
UIDL
(Unique ID Listing) Fragt nach der eindeutigen Kennung der Nachricht.
(Optional)
USER
Übermittelt den Usernamen für die Mailbox (maildrop)
QUIT
Beendet die Verbindung. Löscht alle als gelöscht markierten Mails.

Eine POP3-Sitzung via Telnet

Unter Linux ist üblicherweise ein brauchbarer Telnet-Client vorhanden. Windows-Usern empfehle ich PuTTYtel oder HyperTerminal Private Edition.

Ein POP3-Server ist üblicherweise an Port 110 gebunden.

telnet localhost 110

Verbindungsaufbau

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
+OK QPOP (version 2.2) at 127.0.0.1 starting.

Login

user thomas
+OK Password required for thomas.
pass xxxxxxx
+OK thomas has 8 messages (3371 octets).

Sobald Sie eingeloggt sind, sperrt der POP3-Server Ihr Postfach,
so daß von keiner anderen Stelle Nachrichten entfernt oder verändert werden können. (Transaktionsphase)

Die Grösse aller Nachrichten anzeigen.

LIST
+OK 8 messages (3371 octets)
1 510
2 412
3 413
4 376
5 374
6 409
7 455
8 422
.

Die Grösse der Nachricht Nr. 8 anzeigen.

LIST 8
+OK 8 422

Header und die ersten fünf Zeilen von Nachricht Nr. 8 anzeigen.

TOP 8 5
+OK 422 octets
Return-Path: <thomas>
Received: (from thomas@localhost)
        by localhost (8.8.8/8.8.8) id OAA00987
        for thomas; Tue, 22 Feb 2000 23:55:27 +0100
Date: Tue, 22 Feb 2000 23:55:27 +0100
From: thomas@localhost
Message-Id: <200002221355.OAA00987@localhost>
To: thomas@localhost
Subject: Perl und POP3
X-UIDL: 6be48a973d87a776ed1fd0135e28b0c2

Hallo Thomas,

Weisst du, wie man POP3-Server mit Perl anzapft?

CU
.

Nachricht Nr. 1 komplett (Header und Body) anzeigen.

RETR 1
+OK 510 octets
Return-Path: <thomas>
Received: (from thomas@localhost)
        by localhost (8.8.8/8.8.8) id OAA00917
        for thomas; Tue, 22 Feb 2000 23:47:46 +0100
Date: Tue, 22 Feb 2000 23:47:46 +0100
From: thomas@localhost
Message-Id: <000002221347.OAA00917@localhost>
To: thomas@localhost
Subject: Teste POP3
X-UIDL: 65d35b6764e2fc801f68323ba9288945
Status: U

Hallo Thomas,

spiel nicht soviel an irgenwelchen Servern rum.
Schlaf mal wieder ne Nacht
.

Eindeutige Kennung der Nachricht Nr. 1 ermitteln

UIDL 1
+OK 1 65d35b6764e2fc801f68323ba9288945

Nachricht Nr. 1 löschen

DELE 1
+OK Message 1 has been deleted.

Alle als gelöscht markierten Nachrichten wieder herstellen

RSET
+OK Maildrop has 8 messages (3449 octets)

Wie gross ist die Mailbox jetzt?

STAT
+OK 8 3449

Lebt der Server noch?

NOOP
+OK

Die Verbindung ordungsgemäss beenden.
Als gelöscht markierte Nachrichten werden jetzt gelöscht.

QUIT
+OK Pop server at localhost signing off.

Siehe auch

Nach einer knappen Übersicht der SMTP-Kommandos gemäss RFC 821 führt Sie diese Seite durch eine Telnet-Beispielsitzung mit einem SMTP-Server, die Ihnen zeigen soll, wie Anfragen und Antworten zwischen Client und Server ablaufen.

SMTP Kommandos RFC 821

HELO
EHLO
Anmelden beim Server
MAIL FROM:
Spezifiziert den Absender und leitet das Absenden von Mail ein.
RCPT TO:
Spezifizieren des Empfängers.
DATA
Nachrichteninhalt eingeben und übertragen.
RSET
Abbruch einer laufenden Übertragung und Reset der Verbindung.
SEND FROM:
Übertragung der Nachricht an ein Terminal. (Optional)
SOML FROM:
Übertragung der Nachricht an ein Postfach oder ein Terminal.
SEND OR MAIL
Optional, Enhanced SMTP)
SAML FROM:
Übertragung der Nachricht an ein Terminal und ein Postfach.
SEND AND MAIL
(Optional, Enhanced SMTP)
VRFY
Fragt nach, ob ein bestimmtes Postfach verfügbar ist. (Optional)
EXPN
Fragt nach den Mitgliedern einer Maillingliste. (Optional, Enhanced SMTP)
HELP
Fordert Hilfeinstruktionen an. (Optional, Enhanced SMTP)
NOOP
No Operation, gibt einen positiven Wert zurück, falls der Server noch lebt.
QUIT
Ordnungsgemässes Abmelden beim SMTP-Server.
TURN
Dreht das Verhältnis zwischen Server und Client um. (Optional, Enhanced SMTP)

Eine SMTP-Sitzung via telnet

Unter Linux ist üblicherweise ein brauchbarer Telnet-Client vorhanden. Windows-Usern empfehle ich PuTTYtel oder HyperTerminal Private Edition.

Ein SMTP-Server ist üblicherweise an den Port 25 gebunden.

telnet localhost 25

Verbindungsaufbau

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 Rechnername ESMTP Mailerinformation Datumsangabe

login

EHLO localhost

Antwort der SMTP-Servers

250-Rechnername Hello thomas@localhost [127.0.0.1], pleased to meet you
250-8BITMIME
250-SIZE
250-DSN
250-ONEX
250-ETRN
250-XUSR
250 HELP

Eine Übersicht der Hilfethemen anfordern

HELP
214-This is  XXXXX
214-Topics:
214-    HELO    EHLO    MAIL    RCPT    DATA
214-    RSET    NOOP    QUIT    HELP    VRFY
214-    EXPN    VERB    ETRN    DSN
214-For more info use "HELP <topic>".
214-For local information send email to Postmaster at your site.
214 End of HELP info

Hilfe zu einem bestimmten Thema (HELP topic)

HELP MAIL
214-MAIL FROM: <sender> [ <parameters> ]
214-    Specifies the sender.  Parameters are ESMTP extensions.
214-    See "HELP DSN" for details.
214 End of HELP info HELP MAIL

Absender spezifizieren

MAIL From: dummy
250 dummy... Sender ok

Eingaben zurücksetzen

RSET
250 Reset state

Diesmal den richtigen Absender einsetzen

MAIL From: thomas
250 thomas... Sender ok

Empfänger

RCPT To: info@thomas-fahle.de
250 info@thomas-fahle.de... Recipient ok (will queue)

Einen zweiten Empfänger angeben

RCPT To: info@xyz.de
250 info@xyz.de... Recipient ok (will queue)

Nachrichtentext und ggf. Header eingeben. Ein einzelner Punkt (.) am Anfang einer Zeile beendet die Eingabe.

DATA
354 Enter mail, end with "." on a line by itself
Subject: Teste SMTP

Hallo allerseits,
..........

MFG

.
250 SAA02108 Message accepted for delivery

Sitzung ordungsgemäss beenden

QUIT
221 Rechnername closing connection
Connection closed by foreign host.

Siehe auch

Die für Ubuntu bereitgestellten CPAN-Pakete sind leider nicht sonderlich zahlreich und auch nicht immer ganz taufrisch.

Die Installation mittels

perl -MCPAN -e shell

ignoriert das Debian-Package-Management leider vollständig. Für gestandene Systemadminstratoren ein Alptraum.

Erfreulicherweise gibt es mehrere Möglichkeiten aktuelle CPAN-Module für Debian basierte Linuxe im Einklang mit dem Debian-Paketmanagement zu installieren.

  1. Debianisierte CPAN-Pakete
  2. cpan2dist
  3. dh-make-perl

Methode 1: Debianisierte CPAN-Pakete

Für die Installation von CPAN-Modulen auf Debian basierten Linuxen gibt es bereits aktuelle debianisierte CPAN-Pakete bei http://debian.pkgs.cpan.org/.

Schritt 1: /etc/apt/sources.list anpassen

Dazu einfach folgende Zeile in /etc/apt/sources.list einfügen

deb http://debian.pkgs.cpan.org/debian unstable main

Schritt 2: Repository-Liste aktualisieren

$ sudo apt-get update
....
Ign http://debian.pkgs.cpan.org unstable Release.gpg               
Ign http://debian.pkgs.cpan.org unstable/main Translation-de
Ign http://debian.pkgs.cpan.org unstable Release
Ign http://debian.pkgs.cpan.org unstable/main Packages
Hole:1 http://debian.pkgs.cpan.org unstable/main Packages [32,6kB]
Es wurden 32,6kB in 1s geholt (19,6kB/s)
Paketlisten werden gelesen... Fertig

Schritt 3: Repository durchsuchen

Alle Debian-CPAN-Pakete sind mit dem Prefix cpan- gekennzeichnet, so kann man sie von den Standardpaketen unterscheiden.

$ apt-cache search cpan-
bioperl - Perl tools for computational molecular biology
libcpan-distnameinfo-perl - Extract distribution name and version from a distribution filename
libcpan-mini-perl - create a minimal mirror of CPAN
libparse-cpan-packages-perl - Perl module to parse 02packages.details.txt.gz
libtest-reporter-perl - sends test results to cpan-testers@perl.org
liblingua-en-numbers-ordinate-perl - Perl-Modul zur Konvertierung von Kardinalzahlen in Ordinalzahlen
.....

Schritt 4: CPAN-Module installieren

Beispiel YAML installieren

$ sudo apt-get install cpan-libyaml-perl

Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut       
Reading state information... Fertig
Die folgenden NEUEN Pakete werden installiert:
  cpan-libyaml-perl
0 aktualisiert, 1 neu installiert, 0 zu entfernen und 0 nicht aktualisiert.
Es müssen noch 0B von 47,6kB Archiven geholt werden.
After this operation, 381kB of additional disk space will be used.
WARNUNG: Die folgenden Pakete können nicht authentifiziert werden!
  cpan-libyaml-perl
Diese Pakete ohne Überprüfung installieren [j/N]? j
Wähle vormals abgewähltes Paket cpan-libyaml-perl.
(Lese Datenbank ... 187518 Dateien und Verzeichnisse sind derzeit installiert.)
Entpacke cpan-libyaml-perl (aus .../cpan-libyaml-perl_0.66-1_all.deb) ...
Richte cpan-libyaml-perl ein (0.66-1) ...

Prüfen, ob das Paket installiert wurde.

$ dpkg -l | grep cpan-libyaml
ii  cpan-libyaml-perl	0.66-1	YAML Ain't Markup Language (tm)

Installierte YAML-Version prüfen:

$ perl -MYAML -le "print YAML->VERSION";
0.66

Ein Blick auf YAML auf CPAN, zeigt uns, das die aktuelle Version installiert wurde.

So einfach geht's. Sehr schön.

Parallele Installation der Pakete

Es ist ohne weiteres möglich, debianisierte CPAN-Pakete und Ubuntu Pakete gleichzeitig zu installieren.

$ sudo apt-get install libyaml-perl
$ sudo apt-get install cpan-libyaml-perl

$ dpkg -l | grep yaml
ii  cpan-libyaml-perl	0.66-1	YAML Ain't Markup Language (tm)
ii  libyaml-perl	0.62-1	YAML Ain't Markup Language (tm)

Die beiden Module werden in unterschiedliche Verzeichnisstrukturen installiert, was sich einfach prüfen lässt.

$ dpkg -L libyaml-perl
...
 /usr/share/doc/libyaml-perl 
 /usr/share/perl5/YAML.pm
...
$ dpkg -L cpan-libyaml-perl
...
/usr/share/doc/cpan-libyaml-perl 
/usr/local/share/perl/5.8.8/YAML  
...

Welche Modulversion verwendet wird, hängt vom @INC ab, i.d.R. wird die debianisierte CPAN-Paket-Version vor der Ubuntu-Version geladen.

Das gilt auch für mitgelieferte Komandozeilen-Tools.

$ which ysh 
/usr/local/bin/ysh

$ dpkg -S  /usr/local/bin/ysh 
cpan-libyaml-perl: /usr/local/bin/ysh

$ dpkg -S  /usr/bin/ysh 
libyaml-perl: /usr/bin/ysh

Welches der beiden Tools verwendet wird, hängt von der Umgebungsvariable $PATH ab.

Methode 2: Debian-Pakete mit cpan2dist selbst erstellen

Wer jetzt immer noch nicht fündig geworden ist, kann mit cpan2dist auch selbst Debian-Pakete erstellen.

Zunächst die benötigten Pakete installierten

$ sudo apt-get install cpan-libcpanplus-perl            
$ sudo apt-get install cpan-libcpanplus-dist-build-perl
$ sudo apt-get install cpan-libcpanplus-dist-deb-perl

Beispiel: Debian-Paket für HTML::Template::Compiled erstellen

cpan2dist kann auch alle Abhängigkeiten auflösen (--buildprereq).

$ cpan2dist --format CPANPLUS::Dist::Deb --buildprereq HTML::Template::Compiled
...
Created 'CPANPLUS::Dist::Deb' distribution for HTML::Template::Compiled to:
~/.cpanplus/5.8.8/dist/debian/main/pool/cpan-lib/h/cpan-libhtml-template-compiled-perl/cpan-libhtml-template-compiled-perl_0.91-1_all.deb

Ins das o.g. Verzeichnis wechseln

$ cd ~/.cpanplus/5.8.8/dist/debian/main/pool/cpan-lib/h/cpan-libhtml-template-compiled-perl
$ ls

cpan-libhtml-template-compiled-perl_0.91-1_all.deb  cpan-libhtml-template-compiled-perl_0.91-1_i386.changes
cpan-libhtml-template-compiled-perl_0.91-1.diff.gz  cpan-libhtml-template-compiled-perl_0.91.orig.tar.gz
cpan-libhtml-template-compiled-perl_0.91-1.dsc

und das Paket inspizieren

$ dpkg -I cpan-libhtml-template-compiled-perl_0.91-1_all.deb

 neues Debian-Paket, Version 2.0.
 Größe 66034 Byte: control-Archiv= 1454 Byte.
     405 Byte,    11 Zeilen      control              
    3328 Byte,    35 Zeilen      md5sums              
 Package: cpan-libhtml-template-compiled-perl
 Version: 0.91-1
 Architecture: all
 Maintainer: cpanplus@example.com
 Installed-Size: 452
 Depends: libpathtools-perl | cpan-libpathtools-perl, libtest-simple-perl | cpan-libtest-simple-perl, perl (>= 5.8.8)
 Provides: libhtml-template-compiled-perl
 Section: perl
 Priority: optional
 Description: Simple and fast templating module
  Simple and fast templating module

Das CPAN-Modul kann jetzt mit dpkg installiert werden.

$ sudo dpkg -i cpan-libhtml-template-compiled-perl_0.91-1_all.deb 
Wähle vormals abgewähltes Paket cpan-libhtml-template-compiled-perl.
(Lese Datenbank ... 188201 Dateien und Verzeichnisse sind derzeit installiert.)
Entpacke cpan-libhtml-template-compiled-perl (aus cpan-libhtml-template-compiled-perl_0.91-1_all.deb) ...
Richte cpan-libhtml-template-compiled-perl ein (0.91-1) ...

Prüfen ob das Paket installiert wurde.

$ dpkg -l | grep compiled
ii  cpan-libhtml-template-compiled-perl	0.91-1 Simple and fast templating module

Noch schnell die installierte HTML::Template::Compiled Version prüfen:

$ perl -MHTML::Template::Compiled -le "print HTML::Template::Compiled->VERSION";
0.91

Cool!

Methode 3: dh-make-perl

dh-make-perl ist eine weitere einfache Möglichkeit CPAN-Pakete zu debianisieren.

Zunächst dh-make-perl installieren

$ sudo apt-get install dh-make-perl

Beispiel: Debian-Paket für HTML::Template erstellen

$ mkdir /tmp/debian/
$ cd /tmp/debian/
$ dh-make-perl --build --cpan HTML::Template
...
$  ls -l
drwxr-xr-x 7  ...  HTML-Template-2.9
-rw-r--r-- 1  ...    libhtml-template-perl_2.9-1_all.deb

und das Paket inspizieren

dpkg -I libhtml-template-perl_2.9-1_all.deb 
 neues Debian-Paket, Version 2.0.
 Größe 64106 Byte: control-Archiv= 1044 Byte.
    1052 Byte,    24 Zeilen      control              
     554 Byte,     7 Zeilen      md5sums              
 Package: libhtml-template-perl
 Version: 2.9-1
 Architecture: all
 Maintainer: .............
 Installed-Size: 208
 Depends: perl (>= 5.6.0-16)
 Section: perl
 Priority: optional
 Homepage: http://search.cpan.org/dist/HTML-Template/
 Description: Perl module to use HTML Templates from CGI scripts
  This module attempts to make using HTML templates simple and natural.
  It extends standard HTML with a few new HTML-esque tags - <TMPL_VAR>,
  <TMPL_LOOP>, <TMPL_INCLUDE>, <TMPL_IF>, <TMPL_ELSE> and <TMPL_UNLESS>.
  The file written with HTML and these new tags is called a template.
  It is usually saved separate from your script - possibly even created
  by someone else!  Using this module you fill in the values for the
  variables, loops and branches declared in the template.  This allows
  you to separate design - the HTML - from the data, which you generate
  in the Perl script.
  .
  This module is licensed under the GPL.  See the LICENSE section
  below for more details.
  .
  This description was automagically extracted from the module by dh-make-perl.

Das CPAN-Modul kann jetzt mit dpkg installiert werden.

$ sudo dpkg -i libhtml-template-perl_2.9-1_all.deb 

Welche der drei Methoden soll ich verwenden?

Hier meine persönliche Vorgehensweise:

  1. Top-Favorit:debianisierte CPAN-Pakete.
  2. cpan2dist, falls ich mit Methode 1 nicht fündig werde.
  3. dh-make-perl verwende ich so gut wie nie.

Zufallszahlen erzeugen mit Bordmitteln

Perl stellt die Funktion rand(x), welche eine Zufallszahl zurück liefert, die größer oder gleich 0 und kleiner als x ist, zur Verfügung.

Beispiel: Zufallszahl zwischen 0 und 9,99

#!/usr/bin/perl
use warnings;
use strict;

# Ganzzahlige Zufallszahl zwischen 0 und 9,99
my $zufallszahl = rand(10);
print "Zufallszahl: $zufallszahl\n";

Das Programm erzeugt folgende Ausgabe:

Zufallszahl: 5.26864530825154

Ganzahlige Zufallszahlen erhalten Sie durch Kombination von int() und rand(x). Falls Sie eine Zufallszahl im Bereich 1 bis einschließlich x benötigen, addieren Sie einfach 1 zur Zufallszahl.

Beispiel: Ganzzahlige Zufallszahlen erzeugen

#!/usr/bin/perl
use warnings;
use strict;

# Ganzzahlige Zufallszahl zwischen 0 und 9
my $zufallszahl = int(rand(10));
print "Zufallszahl: $zufallszahl\n";

# Ganzzahlige Zufallszahl zwischen 1 und 10 
$zufallszahl = int(rand(10) +1 );
print "Zufallszahl: $zufallszahl\n";

Das Programm erzeugt folgende Ausgabe:

Zufallszahl: 5
Zufallszahl: 2

Beispiel: Ein Element aus einem Array zufällig auswählen

#!/usr/bin/perl
use warnings;
use strict;

my @Numbers = qw(0 1 2 3 4 5 6 7 8 9);

my $ausgewaehlt;

$ausgewaehlt = int(rand(scalar(@Numbers)));

print "Auswahl: $ausgewaehlt\n";

Das Programm erzeugt folgende Ausgabe:

Auswahl: 2

Beispiel: Ziehen ohne Zurücklegen

Wenn Sie mehrere Elemente zufällig auswählen möchten, bietet sich eine for-Schleife an.

Doppelte Elemente können Sie mit redo in Kombination mit einem Hash entfernen.

Jedes gezogene Element wird in einem Hash getrackt. Falls es bereits gezogen wurde, wird die for Schleife mit redo (Mach's noch einmal) wiederholt. redo erhöht den Schleifenzähler im Gegensatz zu next nicht.

#!/usr/bin/perl
use warnings;
use strict;

# Beispiel Ziehen ohne Zurücklegen (3 aus 16)

my @Numbers = qw(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);

my $ziehung = '';

my %bereits_gezogen = ();
my $gezogen;


for ( 1 .. 3 ) {
	$gezogen = int(rand(scalar(@Numbers)));
        	print "Auswahl: $gezogen\n"; 

        redo if exists $bereits_gezogen{$gezogen};
        $bereits_gezogen{$gezogen}++;
        $ziehung .= $gezogen;	
}

print "Ziehung: $ziehung\n";

Das Programm erzeugt folgende Ausgabe:

Auswahl: 8
Auswahl: 3
Auswahl: 8
Auswahl: 7
Ziehung: 837

Zufallszahlengenerator initialisieren mit srand()

srand() initialisiert den Zufallszahlengenerator, der von rand() verwendet wird. Diese zufällige Initialisierung dient der Erzeugung verschiedener Zufallszahlen bei jedem Aufruf Ihres Programms.

Oder andersherum betrachtet, wenn stets das selbe Seed verwendet wird, werden auch immer die selben Zufallszahlen erzeugt, d.h. wenn jemand das Seed kennt oder erraten kann, kann er auch die nachfolgenden Zufallszahlen erraten.

Zur Verdeutlichung ein einfaches Beispiel:

#!/usr/bin/perl
use warnings;
use strict;

# Sechs Seeds erstellen
my @seeds = (15 .. 20);

# Dreimal "Zufallszahlen" erzeugen
for ( 1 .. 3 ) {
     &generate_pin($_);
     print "\n";
}
exit(0);
###############################################################################
sub generate_pin {
	my $n = shift;
	my $counter = 0;
        my @Numbers = qw(0 1 2 3 4 5 6 7 8 9);
	foreach my $seed (@seeds) {
		############
		srand $seed;
		############
		++$counter;
		my $pin = '';
		my $x;
		for (1 .. 5) {
			$x = int(rand(scalar(@Numbers)));
			$pin .= $Numbers[$x];
		}
		print "PIN Nr: $counter in Runde $n: $pin\n";
	}
}
###############################################################################

Ausgabe.

PIN Nr: 1 in Runde 1: 23184
PIN Nr: 2 in Runde 1: 10933
PIN Nr: 3 in Runde 1: 97673
PIN Nr: 4 in Runde 1: 84323
PIN Nr: 5 in Runde 1: 71173
PIN Nr: 6 in Runde 1: 58813

PIN Nr: 1 in Runde 2: 23184
PIN Nr: 2 in Runde 2: 10933
PIN Nr: 3 in Runde 2: 97673
PIN Nr: 4 in Runde 2: 84323
PIN Nr: 5 in Runde 2: 71173
PIN Nr: 6 in Runde 2: 58813

PIN Nr: 1 in Runde 3: 23184
PIN Nr: 2 in Runde 3: 10933
PIN Nr: 3 in Runde 3: 97673
PIN Nr: 4 in Runde 3: 84323
PIN Nr: 5 in Runde 3: 71173
PIN Nr: 6 in Runde 3: 58813

Es werden jedesmal die selben (!) Zufallszahlen erzeugt.

Rufen Sie niemals srand() auf, wenn Sie nicht genau wissen, was Sie tun.

Perl ruft srand() automatisch für Sie mit einem geeignetem Seed auf, sobald rand() verwendet wird.

Siehe auch

Mit dem CPAN-Modul CPAN::Mini und dem dazugehörigen Kommandozeilen-Tool minicpan von Ricardo Signes ist sehr einfach, eine CPAN Kopie, die nur die aktuellsten Versionen enthält, lokal auf der Festplatte zu erstellen.

Schritt 1: CPAN::Mini installieren

Entweder mit dem Modul CPAN:

$ sudo perl -MCPAN -e shell
install CPAN::Mini

oder für Ubuntu und andere Debian-basierte Linuxe:

$ sudo apt-get install libcpan-mini-perl 

minicpan wird automatisch installiert.

Schritt 2: minicpan konfigurieren

minicpan sollte die Dateien nach Möglichkeit von einen nahegelegenen CPAN-Mirror holen.

Einen nahegelegenen CPAN-Mirror findet man bei http://mirrors.cpan.org/.

Der gewählte Mirror und das lokale Verzeichnis (hier /opt/CPAN-Mini) werden in die Konfigurationsdatei .minicpanrc im Homeverzeichnis eingetragen.

$ cat ~/.minicpanrc
local:  /opt/CPAN-Mini
remote: http://nahe/gelegener/CPAN/Mirror
exact_mirror: 1

Nun kann es auch schon losgehen.

Schritt 3: minicpan starten

Das mitgelieferte Tool minicpan holt und synchronisiert die Dateien vom CPAN.

$ time minicpan
............... Ausgabe unterdrückt
mkdir /opt/CPAN-Mini/authors/id/M/MA/MADGHOUL
authors/id/M/MA/MADGHOUL/XML-OPML-0.26.tar.gz ... updated
authors/id/M/MA/MADGHOUL/CHECKSUMS ... updated
authors/id/M/MI/MIYAGAWA/XML-OPML-LibXML-0.03.tar.gz ... updated
authors/id/S/ST/STEPHENCA/XML-OPML-SimpleGen-0.04.tar.gz ... updated
authors/id/K/KA/KAWASAKI/XML-OverHTTP-0.08.tar.gz ... updated
authors/id/M/MS/MSTROUT/XML-Overlay-0.01.tar.gz ... updated
authors/id/M/MO/MORNI/XML-ParseDTD-0.1.4.tar.gz ... updated
authors/id/M/MS/MSERGEANT/XML-Parser-2.36.tar.gz ... updated
authors/id/E/EB/EBOHLMAN/XML-Parser-EasyTree-0.01.tar.gz ... updated
authors/id/I/IA/IAMCAL/XML-Parser-Lite-Tree-0.03.tar.gz ... updated
authors/id/I/IA/IAMCAL/XML-Parser-Lite-Tree-XPath-0.02.tar.gz ... updated
authors/id/M/MS/MSERGEANT/XML-PYX-0.07.tar.gz ... updated
authors/id/A/AW/AWIN/XML-Parser-Style-RDF-0.01.tar.gz ... updated
authors/id/D/DO/DOWENS/XML-Parser-Wrapper-0.08.tar.gz ... updated
authors/id/A/AH/AHICOX/XML-Parser-YahooRESTGeocode-0.2.tar.gz ... updated
authors/id/M/MU/MURRAY/PPM-2.1.3.tar.gz ... updated
authors/id/M/MS/MSERGEANT/XML-QL-0.07.tar.gz ... updated
mkdir /opt/CPAN-Mini/authors/id/C/CO/CODEHELP
authors/id/C/CO/CODEHELP/XML-QOFQSF-0.04.tar.gz ... updated
............... Ausgabe unterdrückt

real	77m39.156s
user	2m54.787s
sys	0m22.369s

Also knapp 1,5 h für den ersten vollständigen Durchlauf.

Das neue MINI-CPAN belegt weniger als 1GB Plattenplatz. Das passt sogar auf einen kleinen USB-Stick.

$ du -hs CPAN-Mini/
845M	CPAN-Mini/

Schritt 4: Alle paar Tage CPAN-Mini aktualisieren.

$ minicpan
authors/01mailrc.txt.gz ... up to date
modules/02packages.details.txt.gz ... updated
modules/03modlist.data.gz ... up to date
authors/id/C/CF/CFRANKS/Catalyst-Controller-HTML-FormFu-0.03000.tar.gz ... updated
authors/id/C/CF/CFRANKS/CHECKSUMS ... updated
authors/id/D/DD/DDUMONT/Config-Model-TkUI-0.201.tar.gz ... updated
authors/id/D/DD/DDUMONT/CHECKSUMS ... updated
authors/id/C/CF/CFRANKS/DBIx-Class-HTML-FormFu-0.01005.tar.gz ... updated
authors/id/C/CF/CFRANKS/HTML-FormFu-0.03000.tar.gz ... updated
authors/id/C/CF/CFRANKS/HTML-FormFu-Model-DBIC-0.03000.tar.gz ... updated
authors/id/C/CL/CLIVE/Win32-EnvProcess-0.04.tar.gz ... updated
authors/id/C/CL/CLIVE/CHECKSUMS ... updated
cleaning /opt/CPAN-Mini/authors/id/C/CF/CFRANKS/DBIx-Class-HTML-FormFu-0.01004.tar.gz ...done
cleaning /opt/CPAN-Mini/authors/id/C/CF/CFRANKS/HTML-FormFu-0.02004.tar.gz ...done
cleaning /opt/CPAN-Mini/authors/id/C/CL/CLIVE/Win32-EnvProcess-0.03.tar.gz ...done
cleaning /opt/CPAN-Mini/authors/id/D/DD/DDUMONT/Config-Model-TkUI-0.105.tar.gz ...done

mincpan aktualisiert den lokalen Mirror auf die jeweils neusten Modulversionen (updated) und löscht gleichzeitig (cleaning) alte Versionen.

Schritt 5: CPAN konfigurieren

Zum Schluss muss nur noch der Pfad zu MINI-CPAN im Modul CPAN konfiguriert werden.

$ sudo perl -MCPAN -e shell

cpan[1]> o conf urllist file:///opt/CPAN-Mini
Please use 'o conf commit' to make the config permanent!

cpan[2]> o conf commit
commit: wrote '/home/tf/.cpan/CPAN/MyConfig.pm'

cpan[3]> install Bundle::CPAN
CPAN: Storable loaded ok (v2.18)
CPAN: Time::HiRes loaded ok (v1.9711)
Going to read /opt/CPAN-Mini/authors/01mailrc.txt.gz
...

Enjoy!

Siehe auch:

In der Informatik ist eine Prüfsumme (engl.: checksum) eine einfache Maßnahme zur Gewährleistung von Datenintegrität bei der Datenübermittlung oder -speicherung. Sie wird hauptsächlich bei der Datensicherung und bei der Datenübertragung verwendet. (Quelle: Wikipedia: Prüfsumme.)

Nachfolgend finden Sie ein paar Beispiele zur Erstellung von Prüfsummen über Dateien.

Prüfsummen mit Bordmitteln erstellen

Hier bietet sich unpack() an.

#!/usr/bin/perl
use warnings;
use strict;
use Fcntl;  # Standardmodul
{	
        local undef $/;  # Slurp-Mode - Nur in diesem Block

        my $file = '/path/to/some/file';

        sysopen(FH, $file, O_RDONLY) or die $!;
	binmode(FH);
			# Die ganze Datei in den Hauptspeicher einlesen
	my $CompleteFile = <FH>;

	close(FH) or die $!;

	my $checksum;

		# Berechnet die gleiche Prüfsumme wie sum für System V
	$checksum = unpack ("%32C*", $CompleteFile) % 32767;
	print "System V: ", $checksum , "\n";

		# 16-bit Prüfsumme berechnen
	$checksum = unpack ("%16C*", $CompleteFile);
	print "16-bit: ", $checksum , "\n";
		
		# 32-bit Prüfsumme berechnen
	$checksum = unpack ("%32C*", $CompleteFile);
	print "32-bit: ", $checksum , "\n";
}
exit();

Diese Methode setzt voraus, dass die Daten bzw. Dateien in den Hauptspeicher passen. Vorteil dieser Methode: Sie funktioniert überall, wo Perl funktioniert.

CRC-32 Prüfsummen mit String::CRC32 erstellen

String::CRC32 von Sönke J. Peters kann CRC32-Prüfsummen von Zeichenketten und Dateien erzeugen. String::CRC32 erzeugt die gleichen CRC Prüfsummen wie ZModem, PKZip und entsprechende andere Programme.

#!/usr/bin/perl
use warnings;
use strict;
use Fcntl;  # Standardmodul

use String::CRC32;

my $file = '/path/to/some/file';

sysopen(FH, $file, O_RDONLY) or die $!;

binmode(FH);

my $crc = crc32(*FH);

close(FH) or die $!;

print "CRC32: ", $crc , "\n";

String::CRC32 ist in XS implementiert und läuft sehr schnell. Auch hier gilt, dass die Daten in den Hauptspeicher passen müssen.

CRC32 Prüfsummen mit Digest::CRC erstellen

Digest::CRC von Oliver Maul implementiert das Digest-Interface und kann CRC8-, CRC16-, CRC32- und CRC64-Prüfsummen erstellen.

#!/usr/bin/perl
use warnings;
use strict;
use Fcntl;  # Standardmodul
# OO style
use Digest::CRC;

my $file = '/path/to/some/file';
sysopen(FH, $file, O_RDONLY) or die $!;
binmode(FH);

my $ctx = Digest::CRC->new(type=>"crc32");
$ctx->addfile(*FH);
my $digest = $ctx->digest;
print "CRC32: ", $digest , "\n";
$digest = $ctx->hexdigest;
print "CRC32 (HEX): ", $digest , "\n";
close(FH) or die $!;

Die Datei muss hier nicht in den Hauptspeicher passen, addfile kann auch mit Dateien umgehen, die größer sind als der verfügbare Hauptspeicher.

Siehe auch

Hinweis: alarm() steht leider nicht auf allen Betriebssystemen zur Verfügung.

  1. Fall 1: Einfache Notbremse:
    Sie begrenzen die Laufzeit des gesamten Programms mit alarm(x). Das Programm wird nach spätestens x Sekunden kompromisslos beendet.
  2. Fall 2: Sie müssen Aufräumarbeiten im Fehlerfall ausführen:
    1. Setzen Sie einen eigenen SIGALRM-Handler mit einem Unterprogramm. Dieses stirbt mit die("Ihre ganz spezielle Fehlermeldung"), falls ein entsprechendes Signal eintrifft.
    2. Setzen Sie den Timer mit alarm().
    3. Führen Sie die Operation, das Unterprogramm usw. einschliesslich Timer in einem eval-Block {}; aus.
    4. Schreiben Sie geeignete Routinen für den Fehlerfall.

Beispiel Fall 1: Einfache Notbremse

#!/usr/bin/perl
alarm(5);  # Höchstens 5 Sekunden

sleep(6);  # "Langzeitoperation"

liefert "Alarm clock" auf der Console, d.h. das gesamte Programm wird beendet.

Beispiel Fall 2: Aufräumarbeiten im Fehlerfall

#!/usr/bin/perl
use warnings;
use strict;

	# Handler für SIGALRM und Unterprogramm für den Fall der Zeitüberschreitung
	# Ich setze $SIG{ALRM} stets in den eval()-Block.
	# Varianten mit globalen Signalhandlern und Signalhandlern
	# in Unterprogrammen habe ich auch probiert.
	# Es funktioniert auf belasteten Maschinen nicht 
	# zufriedenstellend und manchmal gar nicht.

	# eval-block	 
eval {
	local $SIG{ALRM} = sub { die "Zeitlimit überschritten $!"};

                        # alarm(x) muss in den eval()-Block		
	alarm(10);	# Maximale Zeit: 9 bis 10 Sekunden
	
	&mach_was();	# Kritische Operation
	
	alarm(0);	# alarm zurücksetzen

}; ## end of eval

	# Irgendetwas schiefgelaufen?
if ($@) {
		# Zeitlimit ueberschritten?
		# Stets patternmatching verwenden,
		# nie eq, da $@ in etwa so aussieht:
		# "Zeitlimit überschritten! died at xxx.pl line xxx"
	if ($@ =~ /Zeitlimit/) {
        		# Fehlerbehandlung
		&Behandle_Fehler_aus_Zeitueberschreitung();

	} else {
		# Irgendetwas anderes in eval ist schiefgelaufen
			# alarm zurücksetzen, der läuft noch
		alarm(0);
		
			# Fehlerbehandlung
		&Behandle_sonstigen_Fehler();		
	}

        # Alles hat geklappt
} else {

        &Mach_weiter();
}

exit;

#############################################################
sub Behandle_Fehler_aus_Zeitueberschreitung {
		# Fehlermeldung ausgeben
	print STDERR "Gotcha! Du warst zu langsam, Baby.\n";
	# your code ...
        exit;
}
#############################################################
sub Behandle_sonstigen_Fehler {
        	# Fehlermeldung ausgeben
	print STDERR "$@\n";
        # your code ... 
        exit;
}
#############################################################

Erläuterungen:

Die Funktion alarm(x) sendet ein SIGALRM-Signal nach x Sekunden. Jeder Aufruf deaktiviert den vorhergehenden Timer, mit alarm(0) kann der Timer deaktiviert werden. Rückgabewert ist die verbleibende Zeit des vorhergehenden Timers.

Verwenden Sie genügend grosse Zeitspannen.
Auf manchen Systemen wird das Alarm-Signal jeweils zu Beginn einer Sekunde ausgeführt. Ein alarm(1) könnte also sofort ausgeführt werden, bevor Ihr Unterprogramm überhaupt etwas macht. Ein alarm(3) würde dann in den nächsten 2 bis 3 Sekunden ausgeführt, ein alarm(10) in den nächsten 9 bis 10 Sekunden.

Messen Sie vorher unter Praxisbedingungen die Laufzeit und legen Sie dann eine geeignete Zeitspanne fest.

Für Zeitmessungen steht Ihnen in Perl (times)[0] oder das Standardmodul Benchmark zur Verfügung. Wenn Sie ein UNIX-System verwenden, können Sie auch das time <Programm> verwenden.

Wenn Sie eine genauere Auflösung benötigen, müssen Sie auf Betriebssystemfunktionen oder auf Time::HiRes zurückgreifen.

Beachten Sie, dass eval {} evt. nur einmal kompiliert wird.

Siehe auch:

sysopen() und flock() ermöglichen den sicheren Zugriff auf Dateien aus Perlprogrammen.

Sie finden hier eine ausführliche Beschreibung der Funktionen und ein Beispiel.

Race Conditions beim Öffnen von Dateien vermeiden

Etwas wie

if (-e $file) {
        open(FH,">$file") or die $!;
}

ist praktisch immer ein Programmierfehler. Hier werden zwei Dinge nacheinander getan, die gleichzeitig getan werden müssen. Weiterhin wird der unsichere Aufruf von open() mit zwei Argumenten statt der sichereren Form mit drei Argumenten verwendet.

Eine sichere Methode zum Öffnen von Dateien ist sysopen().

Verwenden Sie also:

use Fcntl;
sysopen(FILEHANDLE, $FileName, $Modus);

oder

sysopen(FILEHANDLE, $FileName, $Modus, $permission);

statt open().

Fcntl importiert u.a. folgende Modi:

O_RDONLY
Lesezugriff
O_WRONLY
Schreibzugriff
O_RDWR
Lesen und Schreiben

Diese Modi können mit folgenden flags durch ein bitweises OR verknüpft werden.

O_CREAT
Datei erzeugen, falls nicht vorhanden
O_EXCL
In Verbindung mit O_CREAT: Fehler zurückgeben, falls Datei bereits existiert
O_APPEND
Append, "Anhängen": der Datei-Pointer wird auf das Ende der Datei gesetzt
O_TRUNC
Truncate: wenn die Datei besteht, wird sie überschrieben
O_NONBLOCK
Non-blocking access
O_NDELAY
Nicht warten
O_SYNC
Die Datei wird im "synchron I/O Modus" geöffnet.
Jeder Schreibzugriff wird den aufrufenden Prozess solange anhalten, bis die Daten auf die angesprochene Hardware geschrieben sind.

open() und sysopen() im Vergleich

Datei zum Lesen öffnen
open(FH, "<", $file) or die $!;
sysopen(FH, $file, O_RDONLY) or die $!;
Datei zum Schreiben öffnen, erzeugen falls nötig, ansonsten trunkieren
open(FH, ">", $file) or die $!;
sysopen(FH, $file, O_WRONLY|O_TRUNC|O_CREAT) or die $!;
sysopen(FH, $file, O_WRONLY|O_TRUNC|O_CREAT, 0600) or die $!;
Datei zum Schreiben öffnen, neue Datei erzeugen, Datei darf noch nicht existieren
sysopen(FH, $file, O_WRONLY|O_EXCL|O_CREAT) or die $!;
sysopen(FH, $file, O_WRONLY|O_EXCL|O_CREAT, 0600) or die $!;
Datei Öffen zum Anhängen, ggf. erzeugen
open(FH, ">>", $file) or die $!;
sysopen(FH, $file, O_WRONLY|O_APPEND|O_CREAT) or die $!;
sysopen(FH, $file, O_WRONLY|O_APPEND|O_CREAT, 0600) or die $!;
Datei Öffen zum Anhängen, Datei muss existieren
sysopen(FH, $file, O_WRONLY|O_APPEND) or die $!;
Datei öffnen für Aktualisierungen (Lesen und Schreiben), Datei muss existieren
open(FH, "+<", $file) or die $!;
sysopen(FH, $file, O_RDWR) or die $!;
Datei öffnen für Aktualisierungen, Datei darf noch nicht existieren
sysopen(FH, $file, O_RDWR|O_EXCL|O_CREAT) or die $!;
sysopen(FH, $file, O_RDWR|O_EXCL|O_CREAT, 0600) or die $!;
Datei öffnen für Aktualisierungen, Datei ggf. erzeugen
sysopen(FH, $file, O_RDWR|O_CREAT) or die $!;
sysopen(FH, $file, O_RDWR|O_CREAT, 0600) or die $!;

Sperren der Datei für andere Instanzen Ihres Programms

Verwenden Sie einfach sysopen() in Kombination mit flock().

use Fcntl qw/:DEFAULT :flock/;

Fcntl importiert nun zusätzlich folgende Konstanten:

LOCK_SH
Shared Lock (Lesezugriff)
LOCK_EX
Exclusive Lock (Schreibzugriff)
LOCK_NB
Non-Blocking Request( don't stall)
LOCK_UN
Lock wieder freigeben

Beispiel:

use Fcntl qw/:DEFAULT :flock/;  # importiert die Konstanten für sysopen() und flock()
       ...
flock(FH,LOCK_EX); # Exklusiver Lock
       ...
flock(FH,LOCK_UN); # Lock wieder freigeben

Der Lock verfällt, wenn Sie ihn explizit wieder freigeben oder wenn Sie die Datei mit close() schliessen.
Innerhalb einer kritischen Operation darf die Datei also keinesfalls, auch nur kurzfristig, geschlossen werden.

flock() wird Schwierigkeiten bereiten, wenn Sie versuchen Dateien über ein Netzwerk, z.B. NFS, zu flocken.

Permissions

Wenn Sie eine Datei neu anlegen und keinen Wert für $permission angeben verwendet Perl 0666 als Datei-Permission. Der Wert für die Dateirechte wird von der aktuellen umask Ihres Prozesses beeinflusst. umask() gibt daher an, welche Rechte nicht gesetzt werden sollen.

Beispiel Logdatei:

Schauen wir uns das Ganze am Beispiel einer Logdatei an.

Die Datei soll zum Schreiben geöffnet werden, neue Daten am Ende der Datei hinzugefügt werden. Falls die Datei nicht existiert, soll sie erstellt werden. Die Datei soll nur für den Besitzer Lese- und Schreibrechte besitzen (0600). Gleichzeitig darf immer nur eine Instanz des Programmes auf die Logdatei zugreifen (flock).

use Fcntl qw/:DEFAULT :flock/;

local *LOG;

my $file = '/pfad/zur/LogDatei';

my $old_umask = umask(0); # umask zurücksetzen und alten Wert speichern

        # Sicheres Öffnen der Datei        
sysopen(LOG, $file, O_WRONLY|O_APPEND|O_CREAT, 0600) or die $!;

         # Exlusives Locking
flock(LOG, LOCK_EX) || die "flock failed $file $!";

print LOG ".........\n";

close(LOG) or warn "close failed $file $!";

        # Seit Perl 5.004 wird mit close() der I/O Puffer geleert.
        # Vorher mussten Sie den Puffer selbst leeren.
        # Das Locking wird ebenfalls aufgehoben.           

umask($old_umask); # umask wieder auf ursprünglichen Wert zurücksetzen

Siehe auch

Über dieses Archiv

Diese Seite enthält alle Einträge von Perl HowTo von neu nach alt.

Mai 2008 ist das vorherige Archiv.

Juli 2008 ist das nächste Archiv.

Aktuelle Einträge finden Sie auf der Startseite, alle Einträge in den Archiven.

Blog Roll

Powered by

Powered by Movable Type 5.2.10

Creative Commons-Lizenz

Creative Commons License
Dieses Weblog steht unter einer Creative Commons-Lizenz.