Theo: Schnelle Liste von Dateien in Unterverzeichnissen

Hallo zusammen,

den Wunsch, eine Liste aller (oder bestimmter) Dateien in einem bestimmten Verzeichnis und aller Unterverzeichnisse zu erzeugen, findet man zu hunderten im Netz. Was dort empfohlen wird, sieht in etwa so aus:

use File::Find;

FileList("C:/test");

sub FileList {
  my $RootPath = shift;
  my @PDF = ();
  find( sub { push @PDF, $File::Find::name if /.pdf$/ }, $RootPath);
  print "$#PDF files\n";
  return(@PDF);
};

Diese Routine benötigt in einem speziellen Fall (ca. 4000 Files in ca. 100 Unterverzeichnissen) ca. 40 Sekunden.
Das ist mir zu langsam.
Also habe ich eine etwas umständlichere Variante geschrieben (s.u.)
Diese Routine benötigt für das identische Verzeichnis ca. 8-10 Sekunden.

Mache ich da oben etwas falsch?
Vielleicht funktioniert die untere Variante nicht auf allen Systemen?
Kann mir das einer erklären...?

FileListFast("C:/test");

sub FileListFast {
  my $RootPath = shift;
  my @PDF = ();
  my @DirList = ();
  my @List = qx(dir "$RootPath" /-C /S); # execute, read directory
  my $Directory = "";
  my @FileList = ();
  foreach (@List) {
    if (/(\d\d.\d\d.\d\d\d\d)\s+(\d\d:\d\d)\s+(\d+)\s(.+).pdf$/) {
      my ($Date, $Time, $Size, $Filename) = ($1, $2, $3, $4);
      push(@PDF,$Directory.$Filename);
    }
    else {
      if (/ Directory of (.+)\n/) {
        $Directory = $1;
        $Directory =~ s/\///g; # change backslash to foreslash
        push(@DirList,$Directory);
      };
    };
  };
  print "$#PDF files in $#DirList directories\n";
  return(@PDF);
};

  1. Kann mir das einer erklären...?

    Nur eine flüchtige Anmerkung: Perl ist nicht der schnellste Kandidat in Sachen Dateisystem gegenüber vergleichsweisen Programmen, die beispielsweise in C geschrieben sind. Zum Archivieren von Fotodateien hatte ich auch mal ein Perl-Script mit File::Find, einem Modul was an sich schon solche Scripts performant macht. Dieses Script hab ich vor einem Jahr als C-Programm geschrieben, es läuft um Größenordnungen schneller als der alte Kumpel in Perl.

    Du verwendest qx(dir ...), das dürfte den Schub ausmachen, denn >dir< ist nicht einmal eine .exe sondern ein Systeminternes Kommando was im Kernel steckt und somit noch flotter ins Dateisystem greift. Der Sachverhalt wird klar, wenn Du eine Mütze voll Dateien mal mit dem Norton-Commander und einmal mit >copy< bewegst, >copy< ist wesentlich schneller (sone Frage hatte ich mal vor Jahren in der Hotline).

    Natürlich ist qx(irgendwas) dann nicht mehr plattformunabhängig.

    Viele Grüße,
    Horst Haselhuhn

    --
    Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
    1. Moin Moin!

      Du verwendest qx(dir ...), das dürfte den Schub ausmachen, denn >dir< ist nicht einmal eine .exe sondern ein Systeminternes Kommando was im Kernel steckt und somit noch flotter ins Dateisystem greift.

      Das ist ja mal großer Quatsch. Unter DOS und Windows ist dir ein Buildin des Kommandointerpreters, sei es command.com oder cmd.exe. Unter Unixen aller Art ist es bestenfalls ein Alias für ls, und das ist ein Programm wie jedes andere. Im Kernel treibt sich das dir-Kommando auf keinen Fall rum.

      Was die Performance angeht:

      Natürlich ist ein in C geschriebenes Programm oft schneller als ein Perl-Script. Aber: Der Aufruf eines externen Programms ist relativ teuer, insbesondere wenn noch ein Kommandointerpreter im Spiel ist.

      File::Find ist als eierlegende Wollmilchsau geschrieben, die find-Funktion hat aktuell elf verschiedene Optionen neben dem "wanted"-Callback, die u.a. filtern, sortieren, vor- und nacharbeiten und die Arbeitsreihenfolge ändern können. Natürlich ist so ein Monster nicht mehr unbedingt schnell.

      Eine kleine while-Schleife mit einem todo-Array grast einen Dateibaum definitiv schneller als File::Find ab und spart den Child-Prozess ein.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
      1. moin,

        [..] Im Kernel treibt sich das dir-Kommando auf keinen Fall rum.

        Zur Erinnerung:

        Die 3 Dateien

        command.com
        io.sys
        msdos.sys

        bilden der Kernel von MS-DOS.

        Viele Grüße,
        Hotte

        --
        Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
        1. 你好 hotti,

          Zur Erinnerung:

          Die 3 Dateien

          command.com
          io.sys
          msdos.sys

          bilden der Kernel von MS-DOS.

          Der Kernel von MS DOS ist die msdos.sys. Der Kernel vom Windows-DOS besteht aus der io.sys, die msdos.sys ist eine Konfigurations-Datei. Die command.com ist ausschließlich der Kommandozeilen-Interpreter und hat mit dem Kernel _nichts_ zu tun.

          再见,
           克里斯蒂安

          --
          http://wwwtech.de/
          <h1>Software error:</h1>
          <pre>DB failure: Access denied for user 'ckruse_wayne'@'localhost' (using password: YES)
          </pre>
          <p>
          For help, please send mail to the webmaster (<a href="mailto:ck1@wwwtech.de">ck1@wwwtech.de</a>), giving this error message
          and the time and date of the error.
          </p>
  2. Hallo,

    ich nehme stark an, das liegt an den stat calls die find() durchführt.