[PERL] Remplacement récursif

Remplacement récursif [PERL] - Perl - Programmation

Marsh Posté le 02-11-2010 à 21:46:38    

Bonjour,
 
Je suis débutant en PERL et je dois faire un script qui doit remplacer toutes les occurences du nombre 34 par le nombre 99 dans tous les fichiers c contenus dans le répertoire courant mais aussi dans tous les autres sous-répertoires (il y en a beaucoup, ce sera fastidieux de le faire à la main). Pour cela j'ai essayé 2 méthodes mais qui n'ont pas abouti :
 
la 1ère :
j'ai créé le script suivant :
 
#!/bin/perl
 
@ARGV= glob "*.c";
 
while (my $ligne = <> ) {
 
$ligne =~ s/34/99/;
 
print $ligne;
}
 
que j'appelle de la manière suivante :
find . -name "*.c" -exec perl -i script.pl {} \;
J'utilise le find pour appliquer le script à tous les fichiers c trouvés mais l'exécution de ce script ne modifie que les fichiers du répertoire courant et pas ceux des sous-répertoires.
 
la 2ème :
j'ai créé le script suivant :
#!/bin/perl
 
use File::Find;
 
sub process_file {
while (my $ligne = <> ) {
$ligne =~ s/34/99/;
print $ligne;
}
}
 
find(\&process_file, $ARGV[0]);
 
que j'appelle de la manière suivante :
perl -i script_2.pl *.c
 
mais la encore même résultat que précédemment, les fichiers des sous-répertoire ne sont pas modifiés.
 
Quelqu'un aurait-il une idée?
 
 
Bob_64

Reply

Marsh Posté le 02-11-2010 à 21:46:38   

Reply

Marsh Posté le 02-11-2010 à 23:17:26    

C'est bizarre, il y a eu presque la même question, très récemment, voir  
http://forum.hardware.fr/hfr/Progr [...] 1277_1.htm
 
Peut-être que le script n'est pas trouvé, et qu'il faudrait lui préciser son chemin avant son nom.
Sinon, on peut aussi ajouter un -type f qui ne fait pas de mal et qui précise que seuls les fichiers doivent être considérés.


Message édité par olivthill le 02-11-2010 à 23:22:54
Reply

Marsh Posté le 03-11-2010 à 10:34:13    

perl -i script_2.pl *.c  
1) ton shell va transformer le *.c en une liste des noms de fichiers .c du répertoire courant, liste passée en argument au script
2) le script perl execute find(\&process_file, $ARGV[0]);  
 $ARGV[0] est le premier des noms de la liste des noms de fichiers .c du répertoire courant
C'est un nom de fichier, pas un nom de répertoire, ce que le find du script attend, donc il est pas surprenant qu'il aille pas récursivement dans les sous répertoires.
Tu as même de la chance que ça marche pour le répertoire courant.
 
Dans ton script perl, il faut
- passer le nom du répertoire comme argument au script
- s'il y en a pas de passé, prendre celui du repertoire courant ( use Cwd;   my $dir = getcwd;)
- verifier que le fichier est bien un fichier et que son nom est en *.c, sinon ne rien faire, au début de process_file
  faire le changement voulu dans le fichier si c'est OK
 
Note sur ton expression régulière: si on veut remplacer les 34 par 99, il faut peut être faire attention a ne pas remplacer 345 par 995 par exemple...
 
A+,


Message édité par gilou le 03-11-2010 à 10:37:23

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 03-11-2010 à 13:16:18    

Un exemple de solution possible

Code :
  1. #!/usr/local/bin/perl -w
  2. use strict;
  3. use warnings;
  4.  
  5. use File::Find;
  6. use Cwd;
  7.  
  8. # si pas d'argument, on prend le répertoire courant
  9. $ARGV[0] = getcwd unless defined ($ARGV[0]);
  10. # On processe la liste des arguments
  11. foreach (@ARGV) {
  12.    #on applique le find si c'est un répertoire seulement
  13.    find(\&process_file, $_) if (-d);
  14. }
  15.  
  16. sub process_file {
  17.    #on ne traite que les fichiers c
  18.    return unless (-f && /.+\.c$/o);
  19.    # on ouvre et on lit le fichier dans un array
  20.    open(my $fh, '<', $_) or return;
  21.    my @lines = <$fh>;
  22.    close($fh);
  23.    # on fait la substitution dans l'array
  24.    map {s/(^|\D)34(?=\D|$)/${1}99/go} @lines;
  25.    # on ouvre et réécrit le fichier
  26.    open($fh, '>', $_) or return;
  27.    print $fh @lines;
  28.    close($fh);
  29. }


Une variante, plus courte, pas nécessairement plus efficace

Code :
  1. #!/usr/local/bin/perl -w
  2. use strict;
  3. use warnings;
  4.  
  5. use File::Find;
  6. use Cwd;
  7. use Tie::File;
  8.  
  9. # si pas d'argument, on prend le répertoire courant
  10. $ARGV[0] = getcwd unless defined ($ARGV[0]);
  11. # On processe la liste des arguments
  12. foreach (@ARGV) {
  13.    #on applique le find si c'est un répertoire seulement
  14.    find(\&process_file, $_) if (-d);
  15. }
  16.  
  17. sub process_file {
  18.    #on ne traite que les fichiers c
  19.    return unless (-f && /.+\.txt$/o);
  20.    # on associe le fichier à un array
  21.    tie my @lines, 'Tie::File', $_ or return;
  22.    # on effectue la substitution
  23.    map {s/(^|\D)34(?=\D|$)/${1}99/go} @lines;
  24.    untie @lines;
  25. }


A+,


Message édité par gilou le 03-11-2010 à 13:51:38

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 11-11-2010 à 19:37:33    

Merci beaucoup pour la réponse et les explications, cela marche à merveille :)
 
Bob_64

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed