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 :

script d'administration : apprentissage par l'exemple ?


Sujet :

Langage Perl

  1. #1
    Invité
    Invité(e)
    Par défaut script d'administration : apprentissage par l'exemple ?
    Bonjour à tous

    Récemment, j'ai eu à accomplir une tache extrêmement simple pour "tagger" des fichiers texte que j'avais dans mon répertoire home, que je peux résumer de la façon suivante :

    1°) prendre un nom de répertoire en argument
    2°) lister (récursivement ou non) les fichiers dans ce répertoire ayant une extension donnée (par exemple, .c ou encore .h)
    3°) appliquer en première ligne de ce fichier un commentaire qui serait de la forme // prefixe_numero_suffixe //, le numéro étant un identifiant unique pour chaque fichier trouvé à l'étape 2

    Intuitivement, ayant déjà développé en python, je me suis servi de ce langage pour faire un script qui fonctionne. Grosso modo une centaine de lignes, avec commentaires, doc et code bien aéré. Donc en 20-30 lignes, tout y est.
    Ensuite je suis tombé sur quelques articles parlant des onelines Perl, et j'ai été très impressionné par la concision du langage dans certains cas.
    Aussi, je me demandais s'il n'y avait pas plus simple ou du moins plus élégant qu'un gros script monolithique, par exemple en combinant un inline perl avec des pipes.
    Bien sûr, je demande plus par gout de l'esthétique et par curiosité qu'autre chose ^^;
    Dernière modification par Invité ; 24/08/2007 à 08h13.

  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
    Citation Envoyé par Bibi218 Voir le message
    1°) prendre un nom de répertoire en argument
    2°) lister (récursivement ou non) les fichiers dans ce répertoire ayant une extension donnée (par exemple, .c ou encore .h)
    3°) appliquer en première ligne de ce fichier un commentaire qui serait de la forme // prefixe_numero_suffixe //, le numéro étant un identifiant unique pour chaque fichier trouvé à l'étape 2
    Quel est cet "identifiant unique" ? Un simple compteur pourrait-il suffire ?
    Dans ce cas, ceci pourrait fonctionner :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    find2perl . -iname '*.[ch]' -eval '$g++; tie my @truc, q(Tie::File), $name; unshift @truc, qq(//prefix-$g-suffix)' | perl -MTie::File
    Tout est fait en Perl, du listing de fichier à la modification. Cette ligne marche sous Windows directement (en remplaçant les ' par des " et en supposant que vous ayez Perl installé).

    Une approche un peu plus Unix utiliserait find pour trouver les fichiers :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    find . -iname '*.[ch]' | perl -i -pe 'if($.==1){$g++;s{^}{//prefix-$g-suffix\n}}'
    (pas testé, il est possible qu'il y ait un petit problème avec $. et ARGV)

    --
    Jedaï

  3. #3
    Invité
    Invité(e)
    Par défaut
    Merci beaucoup

    Pour m'entrainer, j'ai essayé de faire 2 scripts équivalents un peu plus évolués en Perl et Python, et effectivement j'arrive à faire bien plus court.
    J'aurais une petite question qui m'intrigue toutefois

    Supposons que j'aie une chaine qui représente une expression régulière. Comment faire pour l'utiliser en l'état avec les opérateurs s// ou //?

    Dans l'idée, ça donnerait quelque chose du genre

    my $regex = q(^\w{3}\d+$);
    ......
    et ensuite

    $ma_chaine =~ s/$regex//g

  4. #4
    Membre chevronné
    Avatar de Woufeil
    Profil pro
    Étudiant
    Inscrit en
    Février 2006
    Messages
    1 076
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2006
    Messages : 1 076
    Points : 2 004
    Points
    2 004
    Par défaut
    Et bien, dans l'idée, c'est pareil que dans la pratique

    Perl arrive à deviner quand tu mets un $ si il signifie fin de chaîne ou simplement si tu veux interpoler une variable. Enlève juste le q() de ta variable et ça fonctionnera parfaitement
    "En essayant continuellement, on finit par réussir. Donc : plus ça rate, plus on a de chances que ça marche" (devise Shadock)
    Application :

    ainsi qu'à regarder la avant de poser une question.

    La rubrique Perl recrute, contactez-moi.

  5. #5
    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 Woufeil Voir le message
    Enlève juste le q() de ta variable et ça fonctionnera parfaitement
    q(...) est équivalent à '...' c'est donc une très bonne idée de l'utiliser ici (pour éviter l'interpolation de \w ou \d ou $).

    Lorsqu'une regexp est interprété, il y a en fait deux phases : d'abord les variables sont interpolés comme dans une chaîne entre "" (sauf le $ final éventuel), puis la regexp est compilée. De ce fait ton code est parfaitement correct.

    Néanmoins il y a également une deuxième approche à la question, qui est de placer une regexp déjà compilé dans une variable, ceci se fait avec l'opérateur qr// et est parfois préférable pour les performances, ou souhaitable pour la lisibilité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    my $regex = qr/^\w{3}\d+$/;
     
    $ma_chaine =~ s/$regex//g
    # ou
    $chaine =~ $regex;
    # équivalent à
    $chaine =~ m/^\w{3}\d+$/;
    NB: Parfois l'on souhaite intégrer littéralement une chaîne de caractère à une regex (autrement dit sans tenir compte des caractères spéciaux qui pourrait s'y trouver), dans ce cas il faut utiliser quotemeta() sur la chaîne avant de l'intégrer à la regex, ou utiliser les caractère spéciaux \Q et \E qui ont le même effet mais directement dans la regex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    my $quoted = quotemeta($chaine_litterale);
     
    # va matcher un nombre suivi du contenu exact de $chaine_litterale
    $chaine =~ m/\d+\s$quoted/; 
    #pareil, suivi d'un autre mot
    $chaine =~ m/\d+\s\Q$chaine_litterale\E\s\w+/;
    --
    Jedaï

  6. #6
    Membre chevronné
    Avatar de Woufeil
    Profil pro
    Étudiant
    Inscrit en
    Février 2006
    Messages
    1 076
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2006
    Messages : 1 076
    Points : 2 004
    Points
    2 004
    Par défaut
    Honte à moi
    J'ai cru qu'il avait écrit qw (l'équivalent d'une liste de mot donc). En même temps, q() n'est pas utilisé souvent
    "En essayant continuellement, on finit par réussir. Donc : plus ça rate, plus on a de chances que ça marche" (devise Shadock)
    Application :

    ainsi qu'à regarder la avant de poser une question.

    La rubrique Perl recrute, contactez-moi.

  7. #7
    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 Woufeil Voir le message
    Honte à moi
    J'ai cru qu'il avait écrit qw (l'équivalent d'une liste de mot donc). En même temps, q() n'est pas utilisé souvent
    Non, c'est sans doute parce que je l'ai utilisé dans mes scripts au début du sujet. q() et qq() sont très utiles pour les uniligne parce qu'ils ne rentrent pas en conflit avec les conventions de quote du shell.

    --
    Jedaï

  8. #8
    Invité
    Invité(e)
    Par défaut
    Merci beaucoup pour vos réponses à tous les deux
    Je commence à apprécier Perl à dire vrai, même si certains comportements me semblent assez obscurs.

    Dernier en date, si quelqu'un peut m'expliquer la logique ?

    Je cherchais à faire une recherche récursive sur tous les fichiers d'un répertoire. Comme j'aime bien mes commandes UNIX, je suis passé par find2perl, qui m'a renvoyé 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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    use strict;
    use File::Find ();
     
    my $dir = "projet_perl";
     
    # Set the variable $File::Find::dont_use_nlink if you're using AFS,
    # since AFS cheats.
     
    # for the convenience of &wanted calls, including -eval statements:
    use vars qw/*name *dir *prune/;
    *name  = *File::Find::name;
    *dir   = *File::Find::dir;
    *prune = *File::Find::prune;
     
    sub wanted; ## a quoi ca sert ici ??? ca doit pouvoir se retirer si j'ai bien compris
     
    # Traverse desired filesystems
    File::Find::find( { wanted => \&wanted }, $dir );
    exit;
     
    sub wanted {
        my ( $dev, $ino, $mode, $nlink, $uid, $gid );
     
    ## en conservant l'appel a lstat, tout se passe bien
    ## en revanche, si je l'enleve, certains fichiers ne sont plus
    ## detectes bien que valides... Pourquoi ???
     
        ( ( $dev, $ino, $mode, $nlink, $uid, $gid ) = lstat($_) )
          && $File::Find::name =~ /^\Q$dir\E\/.*\z/s
          && ( $File::Find::prune = 1 )
          && -f _ && T _
          && print("$name\n");
    }

  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
    ne sert effectivement à rien ici, il y a peut-être certains cas obscurs où cela sert (après tout find2perl est un générateur automatique, on ne peut pas s'attendre à ce qu'il génère toujours un script parfait).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    ## en conservant l'appel a lstat, tout se passe bien
    ## en revanche, si je l'enleve, certains fichiers ne sont plus
    ## detectes bien que valides... Pourquoi ???
     
        ( ( $dev, $ino, $mode, $nlink, $uid, $gid ) = lstat($_) )
          && $File::Find::name =~ /^\Q$dir\E\/.*\z/s
          && ( $File::Find::prune = 1 )
          && -f _ && T _
    C'est à cause de -f _ : -f "fichier" teste si "fichier" est bien un fichier normal (pas un répertoire ou un périphérique...), -f _ est une écriture spéciale pour dire d'utiliser le résultat du dernier stat() (ou lstat()) pour répondre au test. Autrement dit quand on veut faire une série de test sur un fichier ça économise des appels systèmes (non-négligeable du point de vue performance). Ici évidemment si tu supprimes lstat()...

    --
    Jedaï

  10. #10
    Invité
    Invité(e)
    Par défaut
    Oui, j'avais vu en lisant le Camel Book que $_ correspondait au dernier fichier traité par lstats. Mais comme après une bête erreur, la fonction s'était trouvée commentée et que le find semblait marcher correctement, sur le coup je n'avais pas vu que certains fichiers manquaient à l'appel. J'avais donc supposé que quand find parcourait mon arborescence, la variable $_ était automatiquement assignée pour chaque fichier testé, et que le lstats ne servait qu'à rapatrier les infos.
    Pour ma défense, je n'ai qu'une semaine de Perl à mon actif @_@

Discussions similaires

  1. Réponses: 1
    Dernier message: 06/03/2015, 17h43
  2. Apprentissage de Qemu/LibVirt par l'exemple
    Par Lana.Bauer dans le forum Virtualisation
    Réponses: 0
    Dernier message: 28/02/2014, 19h11
  3. [Débuter en Delphi] Apprentissage par Turbo Pascal
    Par tonedeff dans le forum Débuter
    Réponses: 17
    Dernier message: 01/08/2005, 10h08

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