Na klar: Man kann sich innerhalb der meisten Mailprogramme eine Adressliste mit Spitznamen aus den einzelnen Mails zusammenklauben. Aber wenn man einmal Ordnung schaffen will, ist es zu mühselig, sich durch die Mails zu hangeln.
Aufgabe also:
sort
und
uniq
tun's.
Die Ausgabe der Datei soll umgeleitet werden durch diese beiden
Befehle. Nebenbei kann man gleich nur die Zeilen heraussuchen, die mit
"From: " beginnen.
#!/usr/bin/perl open ADRESSEN, "| sort | uniq"; while (<>) { next unless (/^From: /); print ADRESSEN $_, "\n"; }Hier kommen mehrere Aspekte von Perl zum Tragen:
while
.
$_
.
#!/usr/bin/perl open ADRESSEN, "| sort | uniq"; while (<>) { next unless (/^From: (.+)$/); my $absender = $1; print ADRESSEN $absender, "\n"; }
Der Befehl
my
deklariert und initialisiert eine Variable (default auf den
Leerstring), die nur im sie umschließenden Block Gültigkeit hat
(lexical scoping).
Es fällt auf, dass in dieser Liste zwei verschiedene Arten vorkommen, einen Absender anzugeben. Die eine ist von der Form "From: Hugo Habicht <hugo@habicht.de>", die andere sieht aus wie "From: hugo@habicht.de (Hugo Habicht)".
Beide Schreibarten müssen berücksichtigt werden. Die interessanten Teile sollen durch (Klammer-)Gruppierung zwischengespeichert werden. Dann muss festgehalten werden, um welche Schreibweise es sich handelt.
Das ist nicht so einfach. Der Übersicht halber verwenden wir daher den
Modifizierer x wie in
s///x
. Er bedeutet, dass jeglicher Leerraum ignoriert wird, es sei
denn, der wird explizit angegeben. Auch Kommentare werden
ignoriert. Damit können reguläre Ausdrücke sehr viel übersichtlicher
geschrieben werden.
Unser Skript erweitert sich nun zu
#!/usr/bin/perl -w open ADRESSEN, "| sort | uniq"; while (<>) { next unless (/^From: (.+)$/); my $absender = $1; if ($absender =~ /([^<(]+) # Erst "Freitext" ohne öffnende # spitze oder runde Klammer ( # dann entweder: \(([^)]+)\) # Text in Paar runder Klammern | # oder: <([^>]+)> # Text in Paar spitzer Klammern ) /x) { print ADRESSEN '$1=', $1, '$2=', $2, '$3=', $3, '$4=', $4, "\n"; } else { warn "Fehler beim Parsen der Adresse:\n\t$absender"; } }Beachte hierzu:
print
ohne Stream-Argument schreibt in die Standardausgabe.
$1, $2, $3
... haben Gültigkeit im sie umschließenden Block, werden aber
vom letzten Suchen überschrieben.
/([^<(]+)\((([^)]+)\)|<([^>]+)>)/
. Er bedeutet: Die
erste Gruppierung enthält eine Folge
von mindestens einem (+
) Zeichen, das weder linke spitze noch runde Klammer
ist. Diese Folge wird in
$1
gespeichert. Die zweite
Gruppierung enthält entweder eine Gruppierung, die innerhalb von runden
Klammern steht, oder eine Gruppierung, die innerhalb von spitzen
Klammern steht
(entweder|oder)
. Diese Gruppierung ist eine Folge ohne rechte spitze
bzw. runde Klammer.
Es wurden insgesamt vier Klammerpaare in diesem regulären Ausdruck
gebildet. Interessant nun: Was steht wo drin? Werden überhaupt alle
vier Variablen gefüllt oder nur drei, nur die ersten drei?
Lösung: Es gilt die Regel der Reihenfolge der linken Klammern! Wenn ein Klammerpaar nicht zutrifft, wird in die entsprechende Variable nicht hineingeschrieben.
Das läßt sich hier wunderbar ausnutzen!
#!/usr/bin/perl -w while (<>) { next unless (/^From: (.+)$/); my $absender = $1; if ($absender =~ /([^<(]+) # Erst "Freitext" ohne öffnende # spitze oder runde Klammer ( # dann entweder: \(([^)]+)\) # Text in Paar runder Klammern | # oder: <([^>]+)> # Text in Paar spitzer Klammern ) /x) { # Wenn Inhalt in viertem Klammerpaar, # dann waren es spitze Klammern # und vier ist die Mailadresse my $mailadresse = $4; my $realname = $1; # Wenn Inhalt in drittem Klammerpaar, # dann waren es runde Klammern # und drei ist der Name if ($3) { $mailadresse = $1; $realname = $3; } print ADRESSEN $realname, " hat die Adresse ", $mailadresse, "\n"; } else { warn "Fehler beim Parsen der Adresse:\n\t$absender"; } }
alias {Spitzname} {bürgerlicher Name} {Mailadresse}Die Mailadresse steht dabei in spitzen Klammern.
Man kann also ein Unterprogramm definieren, in dem steht:
sub aliasline { my ($realname, $mailadresse, $nickname) = @_; print "alias $nickname $realname <$mailadresse>\n" }und in das Hauptprogramm anstelle des
print
-Befehls das
Unterprogramm aufrufen.
Wie bekomme ich aber nun den Spitznamen aus dem Namen heraus? Den wird man sich nach wie vor selbst ausdenken müssen. Man kann aber schon einmal dem Skript vorgeben, einen "sinnigen" Spitznamen zu raten, am besten aus dem ersten Wort des bürgerlichen Namens.
Weiter kann noch eine kosmetische Änderung eingebaut werden: Manchmal steht der bürgerliche Name in Anführungszeichen, die für die Adresszeile aber überflüssig sind. Diese Zeichen können auch getilgt werden. Außerdem befindet sich noch Leerraum am Ende des bürgerlichen Namens, den man ebenfalls wegnehmen kann.
#!/usr/bin/perl -w sub aliasline { my ($realname, $mailadresse, $nickname) = @_; print "alias $nickname $realname <$mailadresse>\n" } while (<>) { next unless (/^From: (.+)$/); my $absender = $1; if ($absender =~ /([^<(]+) # Erst "Freitext" ohne öffnende # spitze oder runde Klammer ( # dann entweder: \(([^)]+)\) # Text in Paar runder Klammern | # oder: <([^>]+)> # Text in Paar spitzer Klammern ) /x) { # Wenn Inhalt in viertem Klammerpaar, # dann waren es spitze Klammern # und vier ist die Mailadresse my $mailadresse = $4; my $realname = $1; # Wenn Inhalt in drittem Klammerpaar, # dann waren es runde Klammern # und drei ist der Name if ($3) { $mailadresse = $1; $realname = $3; } $realname =~ s/"//g; $realname =~ s/\s+$//; # Grober Rateversuch für den Spitznamen: # erstes Wort im Namen $realname =~ /(\w+)/; aliasline($realname, $mailadresse, lc($1)); } else { warn "Fehler beim Parsen der Adresse:\n\t$absender"; } }Fertig!