IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

Generer une liste de fichier/dossiers à partir d'un dossier


Sujet :

Langage Perl

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut Generer une liste de fichier/dossiers à partir d'un dossier
    Bonjour,

    j'ai recherché "glob" sur ce forum pour trouver ce qui m'interesse (et ça y répond bien ) mais j'aurai besoin de vos conseils éclairés pour ma situation plus précise:

    j'utilise actuellement une fonction bricolé avec un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    `dir /b /a:-D "$rep"`; # non recursif
    `dir /b /s /a:-D "$rep"`; # Recursif
    dont je ne suis pas très fier qui permet de:
    - renvoyer la liste des dossiers dans un répertoire
    - renvoyer la liste des fichiers dans un répertoire
    - idem récursivement
    La liste renvoyée contient dans les 4 cas des chemins absolus.

    Je voudrai faire une fonction portable (unix/dos), et surtout qui soit efficace au niveau vitesse/consommation mémoire. Car je devrai utiliser cette fonction pour (entre autres) lister récursivement le contenu d'un dossier qui contiendra sur plusieurs niveaux plusieurs dizaines de milliers de fichiers/dossiers.

    J'ai trouvé sur le forum du code File:Find pour la recherche récursive... que pensez-vous de son efficacité ? glob pour le non récursif (mais pas de distinction fichiers/dossier) qui, parait-il, bouffe beaucoup de ressources...

    Merci d'avance pour vos conseils éclairés

    Bon week end.

  2. #2
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    File::Find est très efficace, meilleur que find dans certains cas. glob() n'est pas particulièrement efficace, et la façon standard de lister un répertoire est d'utiliser opendir()/readdir()/closedir(). En particulier readdir() en contexte scalaire ne renvoie les noms de fichier qu'un à la fois ce qui évite de se trainer des listes énormes.

    La question principale c'est à quoi va te servir ce listing de fichier ? As-tu besoin d'effectuer une opération sur chaque ? As-tu juste besoin de les afficher ? (si tu as besoin de les afficher, ais bien conscience que le facteur limitant sera plutôt la vitesse de ton terminal...) De les écrire dans un fichier ?
    Est-il important que les chemins soient absolus ? (File::Spec->rel2abs() est complètement portable, c'est ce que tu devrais utiliser, exemple sur ce script, en conjonction avec File::Find, utilises le plutôt sur les paramètres de find(), ça sera plus efficace que de le mettre dans le callback)

    --
    Jedaï

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Bonjour Jedai, merci pour ta réponse rapide.

    Alors - actuellement - j'utilise cette liste en tant que liste (donc je n'utilise pas les avantages de certaines fonctions avec le contexte scalaire). Je n'ai pas toujours une opération à effectuer sur chaque élément... en fait, tout dépend du script car j'utiliserai cette fonction dans un script commun appelé par d'autres.

    Dans le cas le plus contraignant, le script appelle cette fonction qui renvoit une liste enorme, et ceci est remarquablement long... Cette liste est ensuite travaillée par plusieurs boucles qui organisent la structure des fichiers dans un hachage. En gros, ça fait presque le travail qui devrait être fait par une base de données (simple du genre les fichiers .dir et .pag de DB Perl) mais je devais m'affranchir de la dépendance à des fichiers autres que les fichiers de données (ceux que je liste), de sorte que placer ou supprimer des dossiers dans le systeme ne perturbe en rien le script.
    Au final, je pense à continuer mais en ajoutant un index dans une db perl, qui sera mis à jour de temps en temps pour accélerer les choses.

    Au départ la fonction de listage n'était pas vraiment pensée pour ça et je dois un peu tout revoir. Mais je veux conserver une certaine compatibilité avec le comportement actuel car je ne pourrai pas mettre à jour tous les scripts qui l'utilisent rapidement.
    D'ailleurs, meme l'utilisation de db perl pour construire mon hachage (qui contient la liste retravaillée) me parait relativement longue vu le nombre d'éléments.

    Bon... je sais que ça peut ne pas paraître très clair,mais j'espere que ça précise assez ce dont j'ai besoin...

    Pour conclure, j'utiliserai File::Find pour la liste des fichiers/dossiers récursif, mais y a t il un moyen de l'utiliser non récursivement (juste le contenu du dossier en argument?).

    Pourquoi utiliser File::Spec->rel2abs() alors que File:Find:name renvoit deja un chemin absolu?

  4. #4
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par Splug
    Dans le cas le plus contraignant, le script appelle cette fonction qui renvoit une liste enorme, et ceci est remarquablement long...
    Je pense qu'utiliser les outils que je t'ai indiqué devrait aider pour cette partie.

    Bon... je sais que ça peut ne pas paraître très clair,mais j'espere que ça précise assez ce dont j'ai besoin...
    En fait tu as l'air d'avoir besoin de quelque chose d'assez polyvalent, donc utilisant un callback comme File::Find... Ca t'éviterais de toujours renvoyer une grosse liste, tu peux passer un callback qui construit directement le hash (tu comprends comment faire ça ?). Et tu peux toujours faire une fonction de listage compatible avec l'ancienne avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    use File::Find;
    use File::Spec;
     
    sub list {
      my ($path, $recursive) = @_;
      $path = File::Spec->rel2abs( $path );
     
      my @files;
     
      if( $recursive ) {
        find( { wanted => sub { push @files, $File::Find::name },
          no_chdir => 1 }, $path );
      } else {
        opendir my($dir), $path;
        @files = map { File::Spec->catfile( $path, $_ ) } (grep m/^\.\.?$/, readdir $dir);
        closedir $dir;
      }
      return @files;
    }
    (NB : Tu prétend que ta fonction `dir /b /a:-D "$rep"` renvoie les chemins absolus mais chez moi ce n'est pas le cas, j'ai tout de même fait ma fonction de façon qu'elle respecte cette contrainte)

    Pour conclure, j'utiliserai File::Find pour la liste des fichiers/dossiers récursif, mais y a t il un moyen de l'utiliser non récursivement (juste le contenu du dossier en argument?).
    Non, mais ce n'est pas nécessaire, la série des fonctions *dir() marche très bien.

    Pourquoi utiliser File::Spec->rel2abs() alors que File:Find:name renvoit deja un chemin absolu?
    Ce n'est pas le cas, teste toi-même :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -MFile::Find -e "find( sub { print $File::Find::name, qq(\n) }, '.')"
    --
    Jedaï

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Super, c'est presque un chat ce forum

    Merci bcp pour tes conseils, je pense que j'ai tout ce qu'il me faut, je reviendrai si j'ai d'autres questions/problemes car je ne pourrai pas le tester dans l'immédiat.

    (NB : Tu prétend que ta fonction `dir /b /a:-D "$rep"` renvoie les chemins absolus mais chez moi ce n'est pas le cas, j'ai tout de même fait ma fonction de façon qu'elle respecte cette contrainte)
    En effet, je n'ai mis qu'un extrait de ma fonction pour dire à quel point c'était du bricolage mais la suite ajoutait le dossier passé en argument en préfixe.

    Citation:
    Pourquoi utiliser File::Spec->rel2abs() alors que File:Find:name renvoit deja un chemin absolu?

    Ce n'est pas le cas, teste toi-même :
    Pourtant ceci (récupéré sur ce forum):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    use File::Find;
    use strict;
    my $dossier = "d:/blabla";
    my(@files, @dirs);
    find({wanted => \&push_filename, no_chdir => 0 }, $dossier );
    print "Fichiers: $#files\nDossiers: $#dirs\n$files[0]\n";
     
    sub push_filename {
      push @files, $File::Find::name if -f $_;
      push @dirs, $File::Find::name if -d $_;
    }
    m'affiche un chemin absolu...

  6. #6
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par Splug
    En effet, je n'ai mis qu'un extrait de ma fonction pour dire à quel point c'était du bricolage mais la suite ajoutait le dossier passé en argument en préfixe.
    D'accord, je comprend mieux.

    Citation Envoyé par Splug
    Pourtant ceci (récupéré sur ce forum):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    use File::Find;
    use strict;
    my $dossier = "d:/blabla";
    my(@files, @dirs);
    find({wanted => \&push_filename, no_chdir => 0 }, $dossier );
    print "Fichiers: $#files\nDossiers: $#dirs\n$files[0]\n";
     
    sub push_filename {
      push @files, $File::Find::name if -f $_;
      push @dirs, $File::Find::name if -d $_;
    }
    m'affiche un chemin absolu...
    Oui, mais ce code (de moi d'ailleurs) ne retourne le chemin absolu que parce qu'on lui a passé un chemin absolu ("d:/blabla") en paramètre. Tu remarqueras d'ailleurs que ma fonction utilise cette propriété : je n'utilise pas rel2abs() à l'intérieur du callback de find() mais bien sur le paramètre de find(), ainsi il n'est appelé qu'une seule fois (au lieu d'autant de fois qu'il y a de fichiers).

    Dans l'exemple que je te demandais de tester :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -MFile::Find -e "find( sub { print $File::Find::name, qq(\n) }, '.')"
    le chemin passé en paramètre (".") est relatif et les $File::Find::name le sont aussi en conséquence.

    --
    Jedaï

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Dans l'exemple que je te demandais de tester :
    Code :

    perl -MFile::Find -e "find( sub { print $File::Find::name, qq(\n) }, '.')"

    le chemin passé en paramètre (".") est relatif et les $File::Find::name le sont aussi en conséquence.
    Je comprend maintenant Vive les contextes .

    Merci

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    J'ai essayé cette fonction:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    sub generer_liste_fichiers2 {
      my ($path, $recursive) = @_;
      $path = File::Spec->rel2abs( $path );
      print "Recherche dans $path\n";
      my @files;
     
      if( $recursive ) {
        find( { wanted => sub { push @files, $File::Find::name if -f $_},
          no_chdir => 1 }, $path );
      } else {
        opendir my($dir), $path;
        @files = map { File::Spec->catfile( $path, $_ ) } (grep m/\w$/, readdir $dir);
        closedir $dir;
    	my @files2;
    	foreach (@files) {
    		push @files2, $_ if -f $_;
    	}
    	@files=@files2;
      }
      return @files;
    }
    N'y a t il pas plus efficace que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    my @files2;
    	foreach (@files) {
    		push @files2, $_ if -f $_;
    	}
    	@files=@files2;
    pour separer dossiers et fichiers dans readdir?

  9. #9
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Il y a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    use File::Find;
    use File::Spec;
     
    sub list_files2 {
      my ($path, $recursive) = @_;
      $path = File::Spec->rel2abs( $path );
     
      my @files;
     
      if( $recursive ) {
        find( { wanted => sub { push @files, $File::Find::name if -f $File::Find::name },
          no_chdir => 1 }, $path );
      } else {
        opendir my($dir), $path;
        while( defined( my $file = readdir $dir ) ) {
          next if m/^\.\.?$/;
          $file = File::Spec->catfile( $path, $file );
          push @files, $file if -f $file;
        }
        closedir $dir;
      }
      return @files;
    }
    (Et ce coup-ci je n'ai pas oublié un "not" comme dans ma fonction précédente (maintenant corrigée) ).

    EDIT : Ok, après benchmark la conclusion est que 'dir' est nettement plus rapide que tout ce que tu peux écrire en Perl, la raison en étant en partie le fait que stat (et donc -f) semble être très lent sous Windows (Win32::File est un peu meilleur). J'aimerais bien avoir les résultats sous Linux d'une comparaison avec ls.
    Avec -f, la différence de performance avec 'dir' est abyssale (sur les grosses arborescences), avec Win32::File et en faisant la récursion à la main j'arrive plus ou moins à un facteur 10 (ça dépend pas mal de la taille de l'arborescence)...
    Je crains que pour le multi-plateforme sans effort ce soit rapé...

    --
    Jedaï

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Yes ça marche nickel
    Juste une petite erreur à la ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    next if ($file =~ m/^\.\.?$/);
    car $_ n'est pas utilisé.

    J'ai utilisé la meme pour list_dirs() avec "-d" et "Spec->catdir".

    As tu une idée du facteur de ralentissement de -f et -d ?
    Serait-ce plus rapide (mais certes moins fiable) d'utiliser une regex testant la presence d'une quelconque extension?
    Je ne dispose malheureusement plus du systeme de fichiers contraignant sur lequel devra travailler le script, ... et je ne pourrai le tester moi meme avant 1 semaine.

  11. #11
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par Splug
    As tu une idée du facteur de ralentissement de -f et -d ?
    Enorme, vraiment énorme... Je suppose que sous Linux ce sera beaucoup beaucoup moins sensible (en effet -f ou -d font un stat() sur le fichier, or sous Linux il s'agit d'un appel système tandis que sous Windows ça doit être émulé de façon complexe). Je crois qu'on doit approcher un facteur 50 par rapport à dir() sur de très grosses arborescences (en fait il y a un surcout fixe associé à dir() qui fait que sur de petites arborescences dir() peut apparaître presque équivalent, ou même plus lent, mais asymptotiquement, la différence est horrible).

    J'ai écris ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    use Win32::File qw(GetAttributes DIRECTORY);
     
    sub rec_list {
        my $path = shift;
        my $attr;
        opendir my($dir), $path;
        my @files;
        my $file;
        while( defined( $file = readdir $dir ) ) {
            next if $file =~ m/^\.\.?$/;
            GetAttributes("$path\\$file", $attr);
            if( $attr & DIRECTORY ) {
                push @files, rec_list("$path\\$file");
            } else {
                push @files, "$path\\$file";
            }
        }
        return @files;
    }
    qui est beaucoup plus proche des performances de 'dir', mais reste inférieur... Peut-être y aurait-il moyen d'utiliser Win32::Api pour obtenir un meilleur résultat, j'y jetterais un coup d'oeil.

    Par ailleurs, sur quel OS voudrais-tu utiliser ce programme ? Si c'est du multi-OS, il suffira de vérifier l'OS avant de lancer la solution générique, ou spécifique de l'OS si elle existe. Je pourrai écrire un petit module comme ça.

    --
    Jedaï

  12. #12
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par Splug
    Serait-ce plus rapide (mais certes moins fiable) d'utiliser une regex testant la presence d'une quelconque extension?
    Ce serait plus rapide (énormément, si tu précompiles bien ta regex), mais un peu dangereux (même sous Windows on rencontre des exceptions, sous Linux ça serait inutilisable)... A n'utiliser que si tu es sûr de ne pas avoir de problème à cause de ça. Peut-être pourrais-tu l'appeler unsafe_list_files() et ne l'utiliser que dans des cas précis ?

    --
    Jedaï

  13. #13
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Ce serait plus rapide (énormément, si tu précompiles bien ta regex), mais un peu dangereux (même sous Windows on rencontre des exceptions, sous Linux ça serait inutilisable)... A n'utiliser que si tu es sûr de ne pas avoir de problème à cause de ça. Peut-être pourrais-tu l'appeler unsafe_list_files() et ne l'utiliser que dans des cas précis ?
    Oui, ce serait en ce sens... car je travaille essentiellement sur Windows. Ce serait quand je sais quelles données sont contenues dans mon arborescence.
    Typiquement, dans mon cas où ma fonction appelant "dir" de Dos prenait qq minutes à travailler, je ferais appel à une regex à défaut d'avoir mieux.

    qui est beaucoup plus proche des performances de 'dir', mais reste inférieur... Peut-être y aurait-il moyen d'utiliser Win32::Api pour obtenir un meilleur résultat, j'y jetterais un coup d'oeil.

    Par ailleurs, sur quel OS voudrais-tu utiliser ce programme ? Si c'est du multi-OS, il suffira de vérifier l'OS avant de lancer la solution générique, ou spécifique de l'OS si elle existe. Je pourrai écrire un petit module comme ça.
    Merci pour la nouvelle fonction.
    En fait, comme je l'ai dit, je travaille essentiellement sous Windows, sur des pc un peu vieillots. Mais les données qu'on utilise viennent de stations Sun sous Solaris. Au début, j'ai fait un ensemble de scripts pour Windows, et me rendant compte que j'utilisais souvent les mêmes fonctions (lol), j'ai regroupé pas mal de choses dans des scripts communs. Seulement ces derniers n'ont jamais eu besoin de tourner sur Unix, de plus je n'imaginais pas au départ lister de telles arborescences.
    Aujourd'hui, il s'avérerait bien pratique de pouvoir les exploiter directement sur les stations Sun.

    J'avais effectivement pensé à un test, c'est tres facile via les variables d'environnement (même si peu fiable ? en tout cas sur mes systemes ca marcherait). Mais au final, ça doit rester relativement polyvalent, je ne veux pas de version 'Unix' / 'Windows'. Mais s'il y a moyen, dans la meme fonction, d'optimiser les choses en fonction du systeme, tout en n'utilisant que des modules disponibles dans le core, à la fois ActivePerl et Perl (unix).

    Merci encore pour ton aide.. Je vais etre en deplacement toute la semaine, je ne sais pas encore si j'aurai droit à du WiFi... donc si je ne répond pas dans ces quelques jours, ce n'est pas par manque d'interet .

    Bonne journée

  14. #14
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par Splug
    J'avais effectivement pensé à un test, c'est tres facile via les variables d'environnement (même si peu fiable ? en tout cas sur mes systemes ca marcherait).
    Il suffit de regarder la variable $^O :
    $OSNAME
    $^O

    The name of the operating system under which this copy of Perl was built, as determined during the configuration process. The value is identical to $Config{'osname'}. See also the Config manpage and the -V command-line switch documented in the perlrun manpage.

    In Windows platforms, $^O is not very helpful: since it is always MSWin32, it doesn't tell the difference between 95/98/ME/NT/2000/XP/CE/.NET. Use Win32::GetOSName() or Win32::GetOSVersion() (see the Win32 manpage and the perlport manpage) to distinguish between the variants.
    Pas mal de modules du CPAN font ainsi pour utiliser des méthodes différentes selon l'OS.

    --
    Jedaï

  15. #15
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Bon, alors j'ai pu faire mes essais, et j'ai quelques difficultés:

    En fonction de l'OS, j'utilise ça pour charger Win32::File:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if ($^O =~ m/MSWin/) {
    	require Win32::File;
    	import Win32::File qw(GetAttributes DIRECTORY);
    } # Fin if
    Sauf que ça doit poser probleme dans la ligne d'import car la constante DIRECTORY ne semble pas utilisable dans mon script.

    Du coup, probleme dans rec_list() au niveau de la recursivité.

    Comment dois-je charger le module (de préférence avec le pragma strict) ?

    Merci encore pour tout, jedai

  16. #16
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    if ($^O =~ m/MSWin/) {  
      eval "use Win32::File qw(GetAttributes DIRECTORY)";
      if( $@ ) {
        die "You don't have Win32::File though you should, report this please.\n";
      }
    }
    --
    Jedaï

  17. #17
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Mmh... ça ne change rien chez moi.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    if ($^O =~ m/MSWin/) { 
    	eval "use Win32::File qw(GetAttributes DIRECTORY)"; 
    	if( $@ ) {  
    		die "Vous n'avez pas le module Win32::File comme vous devriez, informez-en votre support ou installez ce module...\n"; 
    	} 
    }
     
    my @fichiers;
    &Win32_listfiles(".",\@fichiers,1);
     
     
    sub Win32_listfiles {
    	# Fonction de listage de fichiers recursive, optimisee pour windows
    	# Rempli une liste passée par référence et renvoit le nombre de fichiers listés
    	my($path,$rListe,$recursive) = @_;
    	$path = File::Spec->rel2abs( $path );
    	printDebug("Win32_listfiles","Recherche dans '$path'...\n(rec=$recursive)",1);
        #my $path = shift;
        my $attr;
        opendir my($dir), $path;
        #my @files;
        my $file;
        no strict;
        if ($recursive) {
    	    while( defined( $file = readdir $dir ) ) {
    		next if $file =~ m/^\.\.?$/;
    		GetAttributes("$path\\$file", $attr);
    		if($attr & DIRECTORY) { 
    			printDebug("Win32_listfiles","$file est un dossier",1);
    		     &Win32_listfiles("$path\\$file",$rListe,$recursive); # Recursivité
    		} else {
    		    push @{$rListe}, "$path\\$file";
    		}
    	    }
        } # Fin if
        else { # Non recursif
    	    while( defined( $file = readdir $dir ) ) {
    		next if $file =~ m/^\.\.?$/;
    		GetAttributes("$path\\$file", $attr);
    		unless ( $attr & DIRECTORY ) {
    		    push @{$rListe}, "$path\\$file";
    		}
    	    }
        } # Fin else
        use strict;
        return $#{$rListe};
    } # Fin sub
    DIRECTORY n'est pas reconnu (erreur avec use strict)

  18. #18
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Ok, reviens en à ta première version, elle doit probablement marcher, par contre écris "sub DIRECTORY;" au début de ton script pour feinter "strict" (il faut qu'il comprenne que DIRECTORY n'est pas un "bareword" (mot "nu", c'est à dire un mot isolé qui n'est ni une fonction, ni un GLOB, ni un mot clé) mais une constante, c'est à dire en Perl la plupart du temps une fonction qui retourne une valeur constante (parce que si cette fonction est définie au moment de la compilation, perl met directement en ligne la constante).

    --
    Jedaï

  19. #19
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut
    Super ça marche

    C'est très intéressant ça, c'est un peu un bug de Perl non ? Y-a-t-il des articles expliquant en détail ce genre de subtilités ?
    Je suis souvent obligé d'écrire use strict après les autres use pour éviter les problème, c'est lié ?

    Voilà donc mon code, d'après ce que tu m'as donné, pour lister des arborescences de fichiers et dossiers, récursivement ou non, sous Linux et Windows.
    Pourras-tu y jeter un oeil et me dire s'il est possible de l'optimiser (en terme de vitesse) ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
     
    if ($^O =~ m/MSWin/) { 
    	eval "use Win32::File qw(GetAttributes DIRECTORY)"; 
    	if( $@ ) {  
    		die "Vous n'avez pas le module Win32::File comme vous devriez, informez-en votre support ou installez ce module...\n"; 
    	}
    sub DIRECTORY;
    }
     
    my @dossiers = &generer_liste_repertoires(".",1); # DOssiers dans le dossier courant
    my @tousFichiers = &generer_liste_fichiers("."); # tous les fichiers à partir du dossier courant
    my $listeDossiers = &generer_liste_repertoires(".",0,1);  # tous les dossiers à partir du dossier courant (récupère une référence de liste).
     
    sub generer_liste_repertoires {
    	# Listage de dossiers, optimisé pour Linux et Windows
    	# $rep: dossier à lister
    	# $non_recursif: explicite (conserver cette condition negative pour compatibilité ascendante)
    	# $reference: si non nul, renvoit la reference de la liste de fichiers
    	my($rep,$non_recursif,$reference)=@_;
    	my @dossiers;
     
    	if ($^O =~ m/Win/) {
    		if (not $non_recursif) {
    			&Win32_listdirs($rep,\@dossiers,1); # Fonction optimisée pour listage recursif de dossiers
    		} # Fin if
    		else {
    			&Win32_listdirs($rep,\@dossiers,0); # Fonction optimisée pour listage recursif de dossiers
    		} # Fin else
    	} # Fin if
    	else { # Linux...
    		if ($non_recursif) {
    			&linux_listfiles($rep,\@dossiers,1,0) ;
    		} # Fin if
    		else {
    			&linux_listfiles($rep,\@dossiers,1,1) ;
    		} # Fin else
    	} # Fin else
     
    	if ($reference) { # C'est mieux!
    		return \@dossiers;
    	} # Finif
    	else { # Conserver pour compatibilité ascendante
    		@_=@dossiers;
    	} # Fin else
    } # Fin sub
     
    sub generer_liste_fichiers {
    	# Listage de fichiers, optimisé pour Linux et Windows
    	# $rep: dossier à lister
    	# $non_recursif: explicite (conserver cette conditionnel negative pour compatibilité ascendante)
    	# $reference: si non nul, renvoit la reference de la liste de fichiers
    	my ($rep,$non_recursif, $reference)=@_;
    	my @fichiers;
    	if ($^O =~ m/MSWin/) {
    		if (not $non_recursif) {
    			&Win32_listfiles($rep,\@fichiers,1); # Fonction optimisée pour listage recursif de fichiers
    		} # Fin if
    		else {
    			&Win32_listfiles($rep,\@fichiers,0); # Fonction optimisée pour listage recursif de fichiers
    		} # Fin else
    	} # Fin if
    	else { # Linux...
    		if ($non_recursif) {
    			&linux_listfiles($rep,\@fichiers,0,0) ;
    		} # Fin if
    		else {
    			&linux_listfiles($rep,\@fichiers,0,1) ;
    		} # Fin else
    	} # Fin else
     
    	if ($reference) { # C'est mieux!
    		return \@fichiers;
    	} # Finif
    	else { # Conserver pour compatibilité ascendante
    		@_=@fichiers;
    	} # Fin else
    } # Fin sub
     
    sub linux_listfiles {
    	# Fonction optimisée Linux pour lister les fichiers/dossiers d'un dossier, recursivement ou pas.
    	# Si $dossiers non nul, liste des dossiers, sinon des fichiers.
    	# Si $recursive non nul, recursif, sinon, ne liste que le dossier passé.
    	# $rListe est la ref de la liste à remplir
    	# Renvoit le nb d'elements listés
      my ($path, $rListe, $dossiers, $recursive) = @_;
      $path = File::Spec->rel2abs( $path );
      printDebug("linux_listfiles","Recherche dans '$path'...\n(dossiers=$dossiers,rec=$recursive)",1);
      my @files;
     
      if( $recursive ) {
    	  if ($dossiers) { # lister les dossiers?
    		  find( { wanted => sub { push @{$rListe}, $File::Find::name if ( ($File::Find::name !~ m/\/\.\.?$/) && (-d $File::Find::name)) },
    			no_chdir => 1 }, $path );
    	  } # Fin if
    	  else { # lister les fichiers
    		  find( { wanted => sub { push @{$rListe}, $File::Find::name if -f $File::Find::name },
    			no_chdir => 1 }, $path );
    	  } # Fin else
      } else {
        opendir my($dir), $path;
        while( defined( my $file = readdir $dir ) ) {
          next if m/^\.\.?$/;
          $file = File::Spec->catfile( $path, $file );
          push @{$rListe}, $file if (((! $dossiers) && (-f $file)) || (($dossiers) && ($file !~ m/\/\.\.?$/) && (-d $file)));
        }
        closedir $dir;
      }
      return $#{$rListe};
    } # Fin sub
     
     
     
    sub Win32_listdirs {
    	# Fonction de listage de dossiers recursive, optimisee pour windows
    	# Rempli une liste passée par référence et renvoit le nombre de dossiers listés
    	my($path,$rListe,$recursive) = @_;
    	$path = File::Spec->rel2abs( $path );
    	printDebug("Win32_listdirs","Recherche dans '$path'...\n(rec=$recursive)",1);
        #my $path = shift;
        my $attr;
        opendir my($dir), $path;
        #my @files;
        my $file;
        no strict;
        if ($recursive) {
    	    while( defined( $file = readdir $dir ) ) {
    		next if $file =~ m/^\.\.?$/;
    		GetAttributes("$path\\$file", $attr);
    		if($attr & DIRECTORY) { 
    			printDebug("Win32_listdirs","$file est un dossier",1);
    			push @{$rListe}, "$path\\$file";
    		     &Win32_listdirs("$path\\$file",$rListe,$recursive); # Recursivité
    		} #Fin if
    	    }
        } # Fin if
        else { # Non recursif
    	    while( defined( $file = readdir $dir ) ) {
    		next if $file =~ m/^\.\.?$/;
    		GetAttributes("$path\\$file", $attr);
    		if ( $attr & DIRECTORY ) {
    		    push @{$rListe}, "$path\\$file";
    		}
    	    }
        } # Fin else
        use strict;
        return $#{$rListe};
    } # Fin sub
     
    sub Win32_listfiles {
    	# Fonction de listage de fichiers recursive, optimisee pour windows
    	# Rempli une liste passée par référence et renvoit le nombre de fichiers listés
    	my($path,$rListe,$recursive) = @_;
    	$path = File::Spec->rel2abs( $path );
    	printDebug("Win32_listfiles","Recherche dans '$path'...\n(rec=$recursive)",1);
        #my $path = shift;
        my $attr;
        opendir my($dir), $path;
        #my @files;
        my $file;
        no strict;
        if ($recursive) {
    	    while( defined( $file = readdir $dir ) ) {
    		next if $file =~ m/^\.\.?$/;
    		GetAttributes("$path\\$file", $attr);
    		if($attr & DIRECTORY) { 
    			printDebug("Win32_listfiles","$file est un dossier",1);
    		     &Win32_listfiles("$path\\$file",$rListe,$recursive); # Recursivité 
    		} else {
    		    push @{$rListe}, "$path\\$file";
    		}
    	    }
        } # Fin if
        else { # Non recursif
    	    while( defined( $file = readdir $dir ) ) {
    		next if $file =~ m/^\.\.?$/;
    		GetAttributes("$path\\$file", $attr);
    		unless ( $attr & DIRECTORY ) {
    		    push @{$rListe}, "$path\\$file";
    		}
    	    }
        } # Fin else
        use strict;
        return $#{$rListe};
    } # Fin sub
    Je pense (mais peut-être que je me trompe?) que l'utilisation de référence pour la fonction Win32_listdirs/files recursive est beaucoup plus rapide que de passer des grosses listes, non ?

    Merci encore

Discussions similaires

  1. Réponses: 1
    Dernier message: 28/01/2015, 15h05
  2. Réponses: 1
    Dernier message: 06/02/2009, 17h58
  3. Réponses: 2
    Dernier message: 01/05/2008, 13h36
  4. Réponses: 7
    Dernier message: 02/07/2007, 14h37
  5. Créer une variable d'environnement à partir d'une liste de fichier
    Par ddams dans le forum Shell et commandes GNU
    Réponses: 2
    Dernier message: 23/02/2007, 20h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo