Kategorien
Allgemein

Perl im Schnelldurchlauf

Beim letzten Treffen habe ich einen Einblick in Perl gegeben.

So richtig kurz war er dann doch nicht, aber anscheinend waren die anderen ByteWerfler doch motiviert genug um einige kritische Fragen zu stellen, die mir bei der Erstellung der Folien gar nicht eingefallen waren.

Kategorien
Allgemein

Log-Dateien lesbar machen

Da hatte ich eine tolle Log-Datei mit ganz vielen Einträgen. Aber vi wollte die Datei nicht richtig anzeigen. Das Problem war, dass alle Einträge in einer Zeile standen, da ich die newlines am Ende der Einträge vergessen hatte. Was nun

Schnell ein Perl-Skript geschrieben, dass die Anfänge der Log-Einträge findet und vor diese ein Neues Zeilen Zeichen einfügt.

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

my $days = qr{Mon|Tue|Wed|Thu|Fri|Sat|Sun};
my $months = qr{Jan|Feb|Mar|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec};
my $date_regex = qr{(?:$days)(?:$months) d{2} d{2}:d{2}:d{2} d{4}};

while (my $line = <>) {
   while ($line =~ m/($date_regex.*?(?=$date_regex))/g) {
      print "n$1";
   }
}
Kategorien
Allgemein

Adressen aus Mails extrahieren

Es scheint ein Volkssport zu sein, sich die Adressbücher in Thunderbird zu löschen. Das Vergnügen ist aber meist nur kurz, denn die Adressen sind nach dem Löschen und weg und werden dennoch gebraucht. Da ist guter Rat teuer.

Oder man hat ein Perl-Skript wie etwa das folgende:

#!/usr/bin/env perl
use strict;
use warnings;
use Mail::Header;

my $head = Mail::Header->new([<>], Modify => 0);
my $bcc = join("", split(/n/, $head->get('BCC')));
my %addresses = ();
while ($bcc =~ m/("[^"]*" <[^>]+>|<[^>]+>|[^,]+),s*/g) {
    my $address = $1;
    if ($address =~ m/(.*) <([^>]*)>/) {
        $addresses{$2}=$1;
    } else {
        $addresses{$1}="";
    }
}

my @sorted_addresses = map { [$_->[1], $addresses{$_->[1]}] }
    sort { $a->[0] cmp $b->[0] }
    map { [lc($_), $_] } keys %addresses;
for my $address (@sorted_addresses) {
  print "$address->[0], $address->[1]n";
};

Noch einfacher ist aber eventuell ein Python-Skript. Auch wenn es ein deprecated Modul benutzt:

#!/usr/bin/env python
import rfc822
f=open('mail_mit_adressen.txt')
m=rfc822.Message(f)
for address in rfc822.AddressList(m['bcc']):
   print ""%s",%s" % address

Beide Ansätze gehen davon aus, dass man noch eine Mail mit allen Addressaten des gelöschten Adressbuches auffindet.

Kategorien
Allgemein

Wie sage ich es meiner Funktion

Ein Kollege fragte mich heute, wie man in Perl nochmal Parameter an eine Funktion übergibt.

Das ist ja im Prinzip ganz einfach. Eine Funktion in Perl erhält als Übergabeparameter eine einfache Liste mit Namen @_. Aber damit fängt es dann erst an.

Nehmen wir mal an, wir haben eine einfache Funktion mit zwei Parametern:

sub twoParamFunction {
    my ($one, $two) = @_;
    print "$one and $twon";
}

Hier wird die Liste @_ einer Liste mit gerade frisch deklarierten Variablen zugewiesen. Es werden also alle zusätzlichen Parameter verworfen. Diese Art der Parameterübergabe ist vermutlich eine der gebräuchlichsten und einfachsten.

Aber natürlich war das nicht die Art der Übergabe, die mein Kollege im Sinn hatte. Also gut, wir können auch benannte Parameter mit Standard-Werten benutzen:

sub hashAsParam {
    my %hash_param = ( 
        'one' => 'Eins',
        'two' => 'Zwei',
        @_ );
    print "$hash_param{one} und $hash_param{two}n";
}

Hier wird die Liste in eine Hashstruktur eingeblendet und so können die benannte Parameter one und two sowohl übergeben, als auch weggelassen werden – dann erhalten sie eben ihre Standardwerte.

Aber ach, das war es auch nicht. Nun gut, wir können beide Methoden mischen:

sub mixedParams {
    my ($one, $two, %optional) = @_;
    print "$one and $twon";
    print join("; ", keys %optional) . "n";
}

Hier werden aus der Parameterlliste die ersten beiden Elemente $one und $two zugewiesen, und der Rest wird wie in hashAsParam einer Hashstruktur aufgehen. Wenn hierbei auch noch Standardwerte vorgegeben werden sollen, kann das in einer extra Zeile geschehen.

Hmhm, das war es auch nicht. Ok, wir können natürlich auch noch die Liste einzeln abarbeiten:

sub oneStep {
    my $one = shift;
    my $two = shift;
    print "$one and $twon";
}

Ja, genau das wars! Aber wo wir jetzt schon so viele Varianten hatten, können wir eine noch zusätzlich anbieten:

sub selectAFew {
    my ($one, $four) = @_[0,3];
    print "$one a $fourn";
}

Äh? Aber ab hier sollte eigentlich spätestens klar sein, dass man perldata und man perlsyn mal wieder besucht werden sollten. Um den ganzen die Krone aufzusetzen, kam mein Kollege eine halbe Stunde später wieder an. Diesmal wollte er aber die Möglichkeiten der Rückgaben aus Perl-Funktionen wissen…

Kategorien
Allgemein

Perl ausprobieren

Immer mal wieder frage ich mich, was ein einfacher Perl-Ausdruck denn nun wirklich macht. Da kommt der eingebaute Debugger genau richtig.

Für einfache Ausdrucke und Versuche ist es natürlich am einfachsten einen Perl-Einzeiler zu benutzen. Also

perl -e 'print "Hallo Weltn"'

Für Ausdrucke, die aber auf Variablen zur Laufzeit angewiesen ist, kann es recht müßig sein die History der Shell zu nutzen, oder eine Datei zu öffnen und dort  zu experimentieren.

Debugger to the rescue

Da bietet sich dann an den Debugger zu benutzen. Aber ich will ja eigentlich keine Datei entwanzen. Das Programm für den Debugger muss aber auch gar nicht gross sein. Eine einfache 0 tut es da auch. Mit perl -d -e 0 gelangt man mit dem folgenden Ausdruck in seine persönliche Perl Spielwiese:

user@rechner:~$ perl -d -e 0
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> $a="hallo"

  DB<2> print "$a weltn"
hallo welt

Wie man sieht, kann man im Debugger Variablen setzen und auf diese im nächsten Ausdruck zugreifen.

Mit dem üblichen Debugger Befehl x – Untersuche (eXamine) diesen Ausdruck/diese Variable – können wunderbar auch geschachtelte Datenstruckturen anschaulich dargestellt werden.

  DB<3> @a=qw(hallo welt)

  DB<4> x @a
0  'hallo'
1  'welt'
  DB<5> x @a
0  ARRAY(0x8404d64)
   0  'hallo'
   1  'welt'
  DB<6>

Macht man einen Fehler, so gibt der Debugger eine Fehlermeldung aus und man hat wieder einen neuen Versuch.

  DB<6> $a[a)
syntax error at (eval 11)[/usr/share/perl/5.8/perl5db.pl:628] line 2, near "a)"
Missing right curly or square bracket at (eval 11)[/usr/share/perl/5.8/perl5db.pl:628] line 4, at end of line
Kategorien
Allgemein

Einfacher Interpreter einer einfachen Sprache

Nachdem Jörg mir ein paar Perl-Bücher geliehen hatte und schon seit längerer Zeit das „Hello World!“ Plakat auf unserem stillen Örtchen hängt, musste ich mich einfach an einen Brainfuck Interpreter versuchen.

Und hier ist das Ergebnis:

#!/usr/bin/perl
{
  package BF;
  use strict;
  use warnings;

  my @REGISTERS=('PP', 'MP', 'LOOP');
  my %OPERATORS=(
      '+' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_memory}[$self->{_registers}{'MP'}]++;
          $self->{_registers}{'PP'}++;
      },
      '-' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_memory}[$self->{_registers}{'MP'}]--;
          $self->{_registers}{'PP'}++;
      },
      '.' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          push @{ $self->{_output} }, $self->{_memory}[$self->{_registers}{'MP'}];
          $self->{_registers}{'PP'}++;
      },
      '>' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_registers}{'MP'}++;
          $self->{_registers}{'PP'}++;
      },
      '<' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_registers}{'MP'}--;
          $self->{_registers}{'PP'}++;
      },
      '[' => sub {
          my ($self) = @_;
          if ($self->{_memory}[$self->{_registers}{'MP'}] > 0) {
              push @{ $self->{_stack} }, $self->{_registers}{'PP'};
          } else {
              $self->{_registers}{'LOOP'}--;
          }
          $self->{_registers}{'PP'}++;
          return;
      },
      ']' => sub {
          my ($self) = @_;
          my $loop_stacktart = pop @{ $self->{_stack} };
          if ($self->{_memory}[$self->{_registers}{'MP'}]) {
              $self->{_registers}{'PP'} = $loop_stacktart;
          } else {
              $self->{_registers}{'PP'}++;
          }
          $self->{_registers}{'LOOP'}++;
      },
  );

  sub new {
      my $class = shift;
      my $data = {
          _registers => { map { $_ => 0 } @REGISTERS },
          _memory => [],
          _stack => [],
          _output => [],
      };
      bless $data, $class;
  };

  sub run {
      my $self = shift;
      while ($self->{_registers}{PP} < $self->{_max_program_size}) {
          my $op_char = $self->{_program}[$self->{_registers}{PP}];
          my $op = $OPERATORS{$op_char};
          $op->($self);
      }
  }

  sub set_memory {
      my ($self, $memory) = @_;
      $self->{_program} = [ split //, $memory ];
      $self->{_max_program_size} = @{ $self->{_program} };
  }

  sub output {
      my $self = shift;
      return join ',', @{ $self->{_output} };
  }

  sub char_output {
      my $self = shift;
      return join '', map { chr($_) } @{ $self->{_output} };
  }
}

package main;
use strict;
use warnings;

use Test::More qw(no_plan);

my %test_data = (
    '++.' => '2',
    '++-.' => '1',
    '++.>+.<+.' => '2,1,3',
    '+++.[-.].' => '3,2,1,0,0',
    '++.[->++[.-]<]' => '2,2,1,2,1',
    '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' => '72,101,108,108,111,32,87,111,114,108,100,33,10',
);
while (my ($prog, $exptected_output) = each %test_data) {
    my $bf = BF->new();
    $bf->set_memory($prog);
    $bf->run();
    is($bf->output(), $exptected_output, $prog);
}

my $bf = BF->new();
$bf->set_memory('++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.');
$bf->run();
is("Hello World!n", $bf->char_output(), "Hello World!");

Eine tolle sinnlose Beschäftigung.

Und wen es interessiert: Das Plakat zum Programm.

Kategorien
Allgemein

Refcards

Wenn man sich immer mal wieder fragt, wie hiess noch mal der Befehl, oder die welche Tastenkombination war die richtige?

Auf http://refcards.com/ gibt es jede Menge etwa A4 grosser PDF Dateien zu unterschiedlichen Themen, wie perl, javascript oder auch Firefox.

Da lohnt es sich auf jeden Fall mal vorbeizuschauen.

Kategorien
Allgemein

Prototypenärger

Warum Prototypen nur in den seltensten Fällen sinn machen.

Nachdem ich vor einiger Zeit schon mal über einen Sprachwechselfehler in einem meiner Perl Skripte gestossen bin.

Ich hatte eine Funktion wie folgt deklariert:

sub func1() {
    my $var = shift;
    ...
}

Und der Perl Compiler mäkelte irgendwas vonwegen too many arguments… Klar das () weist Perl an, das diese Funktion keine Parameter bekommen darf. Also das () entfernt und alles war wieder gut.

Objekte und Prototypen

Dann bin ich jetzt über eine Code Stelle gestossen, die folgendermassen als Methode in einem Objekt deklariert war:

sub method1( $ ) {
    my $self = shift;
    ....
    my $var = shift;
    ...
}

Da wollte der Autor wohl festhalten, dass diese Methode nur einen Parameter annimmt, und hat das $var=shift später übersehen, oder hat sich gedacht, $self=shift würde vorher ausgewertet werden. Es ist aber so, dass in Methodenaufrufen die Protoypen ignoriert werden.

Als Funktion aufgerufen macht die method1() aber auch nicht das was sie soll, da $self mit hoher Wahrscheinlichkeit nicht den richtigen Inhlat hat.

Praktisch sind sie aber doch

Zum Beispiel um eine Funktion wie grep nachzubauen:

sub my_grep(&@) {
    my ($grepper, @list) = @_;
    my @result = ();
    foreach (@list) {
        push @result, $_
          if ($grepper->());
    }
    return @result;
}

Diese Funktion kann dann wie das eingebaute grep benutzt werden:

my @odd = my_grep { $_[0] % 2 } ( 1 .. 20 );
Kategorien
Allgemein

Aliasing in Perl

Da nutzt man seit Jahren Perl, und dann so was…

In Perl werden Schleifenvariablen über Arrays (und Hashes) als Aliase genutzt. Verändert man diese, so verändert man die Inhalt in den Arrays.

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

use Test::More qw(no_plan);

my @names = qw( Felix Urte Sven );
for my $name (@names) {
    $name =~ s/e//;
}

my @new_names = qw( Flix Urt Svn );
is_deeply( @names, @new_names );

Bisher ist mir das nie aufgefallen. Hoffentlich habe ich tatsächlich nirgendwo solche Nebeneffekte produziert.

Oioioioi…

Kategorien
Allgemein

Warum ist immer alles so — dokumentiert

Da will man gerade eben mal ein SSO im Tomcat einbinden.

Und stellt dann fest, dass das SSO von Tomcat auf der Seite selber so gut wie gar nicht beschrieben ist. Nunja, im Netz findet man sehr schnell viele Referenzen auf CAS. Das soll auch einem Servlet Container wie Tomcat in Bezug auf SSO auf die Beine helfen.

Und für Zope soll es auch ein Produkt geben und für Perl und PHP…

Aber wenn es dann um eine einfache Anleitung geht, eine simple Webanwendung mittels einer Realm mit LDAP und dem CAS Server zu verknüpfen, sieht die Welt nicht mehr ganz so rosig aus.

Die Doku ist in einem Wiki und anscheinend vor allem in Google, oder einfach so lang, dass ich die entscheidenden Passagen beim Überfliegen nicht mehr gefunden habe.

Nun haben wir eine CAS ähnliche Lösung selber gestrickt. Aber hoffentlich finde ich doch noch DIE einfache Anleitung.