OVH Cloud OVH Cloud

$File::Find

12 réponses
Avatar
Pierre-Yves
Bonjour,

Je dois parcourir des répertoires de facon récursive afin de construire un
arbre. Le problème est que je n'ai pas besoin de parcourir systématiquement
tous les sous-répertoires (et il y en a beaucoup). Sur base du nom je peux
en éliminer certains: Par exemple à chaque fois qu'il y a un répertoire
"BACKUP" on sait qu'il est inutile d'aller voir ce qu'il y a en-dessous.

/source/
/répertoire1
/répertoire11
/répertoire111
/BACKUP
...
/répertoire12
/BACKUP
/répertoire2
/répertoire21
/BACKUP
...
...

Pour le moment j'utilise un find(&listFolders, $entrypoint)
Et dans la subroutine listFolders je fais un $File::Find::name if -d &&
!/^.$/; ca fonctionne mais c'est horriblement lent et comme c'est appelé
depuis un CGI ce n'est pas possible de laisser le utilisateurs attendre
plusieurs minutes à chaque fois.

J'ai essayé d'ajouter un "next if /\/BACKUP$/;" mais ca n'a pas l'air de
fonctionner.

Si quelqu'un peut m'aider ca serait sympa parce que je débute en perl et je
ne suis pas encore très à l'aise avec ce language.

D'avance merci pour toutes les réponses !

10 réponses

1 2
Avatar
Paul Gaborit
À (at) Mon, 16 Aug 2004 09:23:13 +0200,
"Pierre-Yves" <pyu-at-belbone.be> écrivait (wrote):
Je dois parcourir des répertoires de facon récursive afin de construire un
arbre. Le problème est que je n'ai pas besoin de parcourir systématiquement
tous les sous-répertoires (et il y en a beaucoup). Sur base du nom je peux
en éliminer certains: Par exemple à chaque fois qu'il y a un répertoire
"BACKUP" on sait qu'il est inutile d'aller voir ce qu'il y a en-dessous.


use strict;
use warnings;

use File::Find;

find({wanted => &listFolders,
preprocess => &filtre,
no_chdir => 1},
$ARGV[0]);

sub filtre {
return grep {!m/^BACKUP$/} @_;
}

sub listFolders {
print "$_n" if -d $_;
}


--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>

Avatar
Pierre-Yves
Hello!

Merci pour le code, mais le filtre ne fonctionne pas :
Tous les répertoires sous BACKUP semblent être processés et ajoutés à la
liste :-(



"Paul Gaborit" wrote in message
news:

À (at) Mon, 16 Aug 2004 09:23:13 +0200,
"Pierre-Yves" <pyu-at-belbone.be> écrivait (wrote):
Je dois parcourir des répertoires de facon récursive afin de construire
un


arbre. Le problème est que je n'ai pas besoin de parcourir
systématiquement


tous les sous-répertoires (et il y en a beaucoup). Sur base du nom je
peux


en éliminer certains: Par exemple à chaque fois qu'il y a un répertoire
"BACKUP" on sait qu'il est inutile d'aller voir ce qu'il y a en-dessous.


use strict;
use warnings;

use File::Find;

find({wanted => &listFolders,
preprocess => &filtre,
no_chdir => 1},
$ARGV[0]);

sub filtre {
return grep {!m/^BACKUP$/} @_;
}

sub listFolders {
print "$_n" if -d $_;
}


--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>



Avatar
Paul Gaborit
À (at) Mon, 16 Aug 2004 11:46:57 +0200,
"Pierre-Yves" <pyu-at-belbone.be> écrivait (wrote):
Merci pour le code, mais le filtre ne fonctionne pas :
Tous les répertoires sous BACKUP semblent être processés et ajoutés à la
liste :-(


Ici, ça marche très bien :

% find rep-exemple/ -type d
rep-exemple/
rep-exemple/BACKUP
rep-exemple/BACKUP/toto
rep-exemple/autre
rep-exemple/autre/toto
% ./script.pl rep-exemple
rep-exemple
rep-exemple/autre
rep-exemple/autre/toto
%

...avec le script tel que je l'ai posté.

Avez-vous testé ce script tel quel ?
Sur quel OS ?
Avec quelle version de perl ?

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>

Avatar
oups
Paul Gaborit wrote:
Avez-vous testé ce script tel quel ?
Sur quel OS ?
Avec quelle version de perl ?



Bonjour,

Si cela peut aider, sous XP-SP1, ActivePerl Binary build 809
(This is perl, v5.8.3 built for MSWin32-x86-multi-thread
(with 8 registered patches, see perl -V for more detail)) :

ufind rep-exemple -type d
rep-exemple

rep-exempleautre
rep-exempleautretoto
rep-exemplebackup
rep-exemplebackuptoto

a.pl rep-exemple
rep-exemple

rep-exemple/autre
rep-exemple/autre/toto

Marche très bien.

Cordialement,

PS: ufind = Unix find (cf. http://gnuwin32.sf.net)

--
oups

mail: see http://oupsinet.free.fr/mail.html

Avatar
Paul Gaborit
À (at) Mon, 16 Aug 2004 11:58:50 +0200,
Paul Gaborit écrivait (wrote):
À (at) Mon, 16 Aug 2004 11:46:57 +0200,
"Pierre-Yves" <pyu-at-belbone.be> écrivait (wrote):
Merci pour le code, mais le filtre ne fonctionne pas :
Tous les répertoires sous BACKUP semblent être processés et ajoutés à la
liste :-(


Ici, ça marche très bien :

% find rep-exemple/ -type d
rep-exemple/
rep-exemple/BACKUP
rep-exemple/BACKUP/toto
rep-exemple/autre
rep-exemple/autre/toto
% ./script.pl rep-exemple
rep-exemple
rep-exemple/autre
rep-exemple/autre/toto
%

...avec le script tel que je l'ai posté.

Avez-vous testé ce script tel quel ?
Sur quel OS ?
Avec quelle version de perl ?


Les réponses fournies (par mail) furent :

Oui.
Solaris 2.6.
perl 5.005_03

Donc voici une solution 'à la main' (je n'ai plus de perl 5.005_03 pour
retrouver comment fonctionnait File::Find à cette époque) :

use strict;
use warnings;

sub list_dir {
my @dirs = @_;
while(@dirs) {
my $dir = pop @dirs;
print "$dirn";
local *DIR;
opendir DIR, $dir
or die "Can't read directory $dir: $!n";
foreach my $name (readdir(DIR)) {
next if $name =~ m/^..?$/;
next if $name =~ m/^BACKUP$/; # c'est le filtre
if (-d "$dir/$name") {
push @dirs, "$dir/$name";
}
}
closedir DIR;
}
}

list_dir($ARGV[0]);

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>


Avatar
Pierre-Yves
Pour info: Je viens juste de tester le même code sur une linuxbox avec perl
5.8.2 et là ca fonctionne bien.


"Paul Gaborit" wrote in message
news:

À (at) Mon, 16 Aug 2004 11:58:50 +0200,
Paul Gaborit écrivait (wrote):
À (at) Mon, 16 Aug 2004 11:46:57 +0200,
"Pierre-Yves" <pyu-at-belbone.be> écrivait (wrote):
Merci pour le code, mais le filtre ne fonctionne pas :
Tous les répertoires sous BACKUP semblent être processés et ajoutés à
la



liste :-(


Ici, ça marche très bien :

% find rep-exemple/ -type d
rep-exemple/
rep-exemple/BACKUP
rep-exemple/BACKUP/toto
rep-exemple/autre
rep-exemple/autre/toto
% ./script.pl rep-exemple
rep-exemple
rep-exemple/autre
rep-exemple/autre/toto
%

...avec le script tel que je l'ai posté.

Avez-vous testé ce script tel quel ?
Sur quel OS ?
Avec quelle version de perl ?


Les réponses fournies (par mail) furent :

Oui.
Solaris 2.6.
perl 5.005_03

Donc voici une solution 'à la main' (je n'ai plus de perl 5.005_03 pour
retrouver comment fonctionnait File::Find à cette époque) :

use strict;
use warnings;

sub list_dir {
my @dirs = @_;
while(@dirs) {
my $dir = pop @dirs;
print "$dirn";
local *DIR;
opendir DIR, $dir
or die "Can't read directory $dir: $!n";
foreach my $name (readdir(DIR)) {
next if $name =~ m/^..?$/;
next if $name =~ m/^BACKUP$/; # c'est le filtre
if (-d "$dir/$name") {
push @dirs, "$dir/$name";
}
}
closedir DIR;
}
}

list_dir($ARGV[0]);

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>




Avatar
Paul Gaborit
À (at) Mon, 16 Aug 2004 16:50:22 +0200,
"Pierre-Yves" <pyu-at-belbone.be> écrivait (wrote):
Pour info: Je viens juste de tester le même code sur une linuxbox avec perl
5.8.2 et là ca fonctionne bien.


En fait, ça marche bien avec les versions "récentes" de File::Find (celles qui
datent de la sortie de perl 5.6). Pour les versions préhistoriques (du siècle
dernier), c'est moins évident. ;-)

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>

Avatar
Laurent Wacrenier
Paul Gaborit écrit:
if (-d "$dir/$name") {
push @dirs, "$dir/$name";
}


Attention aux liens symboliques.

Avatar
Paul Gaborit
À (at) Fri, 20 Aug 2004 12:30:41 +0000 (UTC),
Laurent Wacrenier <lwa@ teaser . fr> écrivait (wrote):
Paul Gaborit écrit:
if (-d "$dir/$name") {
push @dirs, "$dir/$name";
}


Attention aux liens symboliques.


Oui. Avec ce test, les liens symboliques vers des répertoires sont considérés
comme de vrais répertoires. Ce qui peut amener à des suprises (doublons) voir
un plantage si les liens sont récursifs (arborescence semi-infinie).

Pour ne pas suivre les liens symboliques, on peut transformer le test en :

if (-d "$dir/$name" and not -l _) {
push @dirs, "$dir/$name";
}

(L'utilisation de _ permet de ne pas faire appel à 'stat' deux fois.)

Si on veut absolument suivre les liens symboliques, on peut conserver cette
version dans la mesure où on accepte d'éventuels doublons et si on est *sûr*
qu'il n'y a pas de liens récursifs.

Sinon, si on veut suivre les liens symboliques sans affichage des doublons
et/ou sans tomber dans le piège d'un éventuel lien récursif, il faut
construire un hash de tous les répertoires (réels) visités pour éviter de les
revisiter (c'est ce que fait File::Find dans sa version actuelle).

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>


Avatar
Laurent Wacrenier
Paul Gaborit écrit:
Pour ne pas suivre les liens symboliques, on peut transformer le test en :

if (-d "$dir/$name" and not -l _) {
push @dirs, "$dir/$name";
}

(L'utilisation de _ permet de ne pas faire appel à 'stat' deux fois.)


Ça ne marchera pas. Si le stat dit que c'est un répertoire,
alors ce ne sera pas un lien (info qu'on peut avoir avec un lstat())
Comme ça, ça devrait être mieux :

if (not -l "$dir/$name" and -d _) { ... }

1 2