2009-11-23 4 views
2

J'ai un dossier nommé Lib et j'utilise le module File :: Find pour rechercher ce dossier dans le dir dir entier, D:\. Cela prend du temps pour chercher, même 5 minutes si le disque a beaucoup de sous-répertoires. Comment puis-je effectuer une recherche plus rapide de Lib pour que cela se fasse en quelques secondes?Comment puis-je rendre le fichier de Perl :: Find plus rapide?

Mon code ressemble à ceci:

find(\&Lib_files, $dir); 
    sub Lib_files 
    { 
     return unless -d; 
     if ($_=~m/^([L|l]ib(.*))/) 
     { 
      print"$_"; 
     } 
     return; 
    } 
+3

S'il vous plaît poster le code que vous avez effectivement compilé et exécuté. –

+0

Pourquoi est-ce DOWNVOTED ?! Je suis confronté au même problème. 'File :: Find' est très lent. – unixman83

+0

Assurez-vous qu'une tâche d'arrière-plan ne s'exécute pas sous le planificateur de tâches Windows. C'était mon plus gros problème quand Perl prenait 5 minutes pour lister un répertoire. – unixman83

Répondre

20

Recherche sur le système de fichiers sans index est pré-existante IO lié. Sinon, les produits allant de locate à Windows Desktop Search n'existeraient pas.

Tapez D:\> dir /b/s > directory.lst et d'observer le temps nécessaire à l'exécution de cette commande. Vous ne devriez pas vous attendre à battre cela sans indexer les fichiers en premier.

Une amélioration majeure que vous pouvez faire est d'imprimer moins souvent. Une légère amélioration est de ne pas utiliser la capture entre parenthèses si vous n'allez capturer:

my @dirs; 

sub Lib_files { 
    return unless -d $File::Find::name; 
    if (/^[Ll]ib/) { 
     push @dirs, $File::Find::name; 
    } 
    return; 
} 

Sur mon système, un script simple à l'aide File::Find pour imprimer les noms de tous les sous-répertoires sous mon répertoire personnel avec environ 150 000 fichiers prend quelques minutes à courir par rapport à dir %HOME% /ad/b/s > dir.lst qui se termine en environ 20 secondes.

Je serais enclin à utiliser:

use File::Basename; 

my @dirs = grep { fileparse($_) =~ /^[Ll]ib/ } 
      split /\n/, `dir %HOME% /ad/b/s`; 

qui a terminé en moins de 15 secondes sur mon système.

S'il y a une chance il y a une autre dir.exe dans %PATH%, cmd.exe « s intégré dans dir ne sera pas invoquée. Vous pouvez utiliser qx! cmd.exe /c dir %HOME% /ad/b/s ! pour vous assurer que le droit dir est appelé.

+4

+1 pour ne pas utiliser les parenthèses de capture - mais dans l'ensemble, il s'agira probablement d'un effet de second ordre par rapport au temps d'accès au disque. –

+0

@Jonathan Leffler: Oui, je devrais corriger la façon dont j'ai formulé cela parce que les économies les plus importantes viendront de ne pas imprimer si souvent. Cependant, il sera difficile de battre 'qx'dir d: \/ad/b/s'' pour ce genre de chose. –

+0

Vous ne voulez pas dire '/ [Ll] ib /'? '[L | l]' est (grossièrement) équivalent à '(?: L | \ || l)', pas '(?: L | l)'. –

-1

Que diriez-vous de ne pas utiliser module File :: Find

use Cwd; 
sub find{ 
    my ($wdir) = shift; 
    my ($sdir) = &cwd; 
    chdir($wdir) or die "Unable to enter dir $wdir:$!\n"; 
    opendir(DIR, ".") or die "Unable to open $wdir:$!\n"; 
    foreach my $name (readdir(DIR)){ 
     next if ($name eq "."); 
     next if ($name eq ".."); 
     if (-d $name){ 
      &find($name); 
      next; 
     } 

     print $name ."\n"; 
     chdir($sdir) or die "Unable to change to dir $sdir:$!\n"; 
    } 
    closedir(DIR); 
} 
&find("."); 
+0

Une façon d'optimiser cela consisterait à stocker chaque nom de répertoire dans une variable locale afin de pouvoir fermer le descripteur de répertoire avant d'être récursif. L'exemple donné, malheureusement, tient le handle de chaque répertoire ouvert comme il inspecte les sous-répertoires et si vous avez un chemin très profond, vous pourriez affamer votre système de poignées. –

+0

** 1) ** N'utilisez pas '&' pour invoquer des sous-marins, sauf si vous savez pourquoi. Voir http://perldoc.perl.org/perlsub.html ** 2) ** '$ name = ~/^ \. \.? \ Z /' est parfaitement bien. ** 3) ** Avez-vous chronométré votre code? Pourquoi pensez-vous que ce serait plus rapide? –

+0

laissez-moi vous dire pourquoi 1) & est pour une raison historique. Dans les temps anciens, je l'ai écrit comme ça.! 2) Je veux juste utiliser la comparaison de chaînes et pas regex, quel est votre problème? 3) Avez-vous le temps le mien? 4) car il n'appelle pas le module find mais utilise la construction interne de Perl. Raisonnable? – ghostdog74

Questions connexes