[Dresden-pm] Nachfrage wegen rätselhaften Verhalten von Perl

A. Pagaltzis pagaltzis at gmx.de
Son Jan 1 08:49:24 PST 2006


Hallo Hans-Dietrich,

* Hans-Dietrich Kirmse <hd.kirmse at gmx.de> [2006-01-01 16:00]:
> ich möchte alle Listenteilnehmern alle Gute für 2006 wünschen

Frohes Neues. :-)

> # kopiert den Inhalt eines Verzeichnisses in ein anderes
> sub copydir
> {
>   my $quelle = shift;
>   my $ziel   = shift;
> 
>   my $recht  = 0777;
> 
>   find (\&kopieren,$quelle);
> 
>   # ist sonst die Routine "wanted", was aber nichts zur Sache tut
>   sub kopieren

Das funktioniert nie. Du hast hier eine benannte Closure; eine
solche wird an die lexikalischen Variablen des ersten Aufrufs
gebunden.

Du müsstest hier eigentlich Warnungen der Form

    Variable "$quelle" will not stay shared at $script line $x.
    Variable "$ziel" will not stay shared at $script line $y.

erhalten haben.

Das behebst du einfach, indem du stattdessen eine anonyme
Funktion verwendest.

>   {
>     my $reststring;  # der Pfad zur Datei gekürzt um den Pfad zur Quelle
> 
>     # wenn es nicht das Ausgangsverzeichnis (die Quelle) ist
>     if ($quelle ne $File::Find::name) {
>       # dann holen wir uns den "Rest" des Dateinamens nach der Quelle
>       if ($File::Find::name =~ /^$quelle(.*)$/) { $reststring = $1 };

Das ist umständlich und fehlerträchtig. Stattdessen solltest
einfach find() mit der Option no_chdir aufrufen und dann $_
verwenden.

>       # wenn das gefundene Element ein Verzeichnis ist
>       if (-d $File::Find::name) {
>         # dann legen wir im Ziel ein neues Verzeichnis an
>         mkdir($ziel.$reststring,$recht);
>       } else {
>         # dann kopieren wir diese Datei in dieses Ziel
>         copy($File::Find::name,$ziel.$reststring);

Bitte immer File::Spec verwenden, um Pfade zusammenzubasteln.

> die Variable "$recht" soll in den Konfigurationsteil, […] gibt
> man die dann als Sting an oder wie macht man das korrekt?

Du willst ziemlich sicherlich die Rechte aus dem Quellbaum auf
den Zielbaum übertragen, nicht einen konstanten Wert verwenden;
und schon garnicht 777, es sei denn, die Sicherheit des Servers
ist dir piepe. :-)

Summa summarum:

    use File::Find;
    use File::Copy;
    use File::Spec::Functions qw( abs2rel catfile canonpath );
    use Fcntl qw( :mode );

    sub copydir {
        my ( $src, $dst ) = @_;

        find( {
            no_chdir => 1,
            wanted   => sub {
                my $relsrc  = abs2rel( $_, $src );
                my $fulldst = canonpath( catfile( $dst, $relsrc ) );
                my $srcmode = ( stat $_ )[ 2 ];
                my $srcperm = S_IMODE( $srcmode );
                if( S_ISDIR( $srcmode ) ) {
                    mkdir $fulldst, $srcperm;
                }
                else {
                    copy $_, $fulldst;
                    chmod $srcperm, $fulldst;
                }
            },
        }, $src );
    }

$relsrc ist hier der Pfad der zu bearbeitenden Datei aus $_
relativ zum Quellverzeichnis in $src. catfile() fügt diesen Pfad
ans Zielverzeichnis an; allerdings wird dabei aus dem obersten
Verzeichnis, »quelle/«, dabei »ziel/.« – das wird erst vermittels
canonpath() als »ziel« aufgelöst, ehe das Ergebnis in $fulldst
kommt.

In diesem Code fehlt allerdings ebenso wie auch in deinem
ursprünglichen jegliche Fehlerbehandlung.

Gruss,
-- 
#Aristoteles
*AUTOLOAD=*_;sub _{s/(.*)::(.*)/print$2,(",$\/"," ")[defined wantarray]/e;$1};
&Just->another->Perl->hacker;