Question

J'ai essayé de coder un script Perl pour substituer du texte sur tous les fichiers source de mon projet. J'ai besoin de quelque chose comme:

perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi" *.{cs,aspx,ascx}

Mais cela analyse tous les fichiers d'un répertoire récursivement .

Je viens de commencer un script:

use File::Find::Rule;
use strict;

my @files = (File::Find::Rule->file()->name('*.cs','*.aspx','*.ascx')->in('.'));

foreach my $f (@files){
    if ($f =~ s/thisgoesout/thisgoesin/gi) {
           # In-place file editing, or something like that
    }
}

Mais maintenant je suis coincé. Existe-t-il un moyen simple d’éditer tous les fichiers en place à l’aide de Perl?

Veuillez noter que je n'ai pas besoin de conserver une copie de chaque fichier modifié. Je les ai tous subversions =)

Mise à jour : j'ai essayé cette solution sur Cygwin ,

perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi" {*,*/*,*/*/*}.{cs,aspx,ascx

Mais il semble que ma liste d'arguments a explosé à la taille maximale autorisée. En fait, je reçois des erreurs très étranges sur Cygwin ...

Était-ce utile?

La solution

Si vous assignez @ARGV avant d'utiliser *ARGV (le diamant <>), $^I / -i travaillera sur ces fichiers au lieu de ce qui a été spécifié sur la ligne de commande.

use File::Find::Rule;
use strict;

@ARGV = (File::Find::Rule->file()->name('*.cs', '*.aspx', '*.ascx')->in('.'));
$^I = '.bak';  # or set `-i` in the #! line or on the command-line

while (<>) {
    s/thisgoesout/thisgoesin/gi;
    print;
}

Cela devrait faire exactement ce que vous voulez.

Si votre modèle peut s'étendre sur plusieurs lignes, ajoutez un undef $/; avant le <=> afin que Perl opère simultanément sur un fichier entier au lieu d'une ligne à la fois.

Autres conseils

Vous pourriez être intéressé par Fichier :: Transaction :: Atomic ou Fichier :: Transaction

Le SYNOPSIS pour F :: T :: A est très similaire à ce que vous essayez de faire:

  # In this example, we wish to replace 
  # the word 'foo' with the word 'bar' in several files, 
  # with no risk of ending up with the replacement done 
  # in some files but not in others.

  use File::Transaction::Atomic;

  my $ft = File::Transaction::Atomic->new;

  eval {
      foreach my $file (@list_of_file_names) {
          $ft->linewise_rewrite($file, sub {
               s#\bfoo\b#bar#g;
          });
      }
  };

  if ($@) {
      $ft->revert;
      die "update aborted: $@";
  }
  else {
      $ft->commit;
  }

Ajoutez cela à File :: Find que vous avez déjà écrit et vous devriez être prêt à partir.

Vous pouvez utiliser Tie :: File pour accéder de manière évolutive à des fichiers volumineux et les modifier sur place. Voir la page de manuel (man 3perl Tie :: File).

Changer

foreach my $f (@files){
    if ($f =~ s/thisgoesout/thisgoesin/gi) {
           #inplace file editing, or something like that
    }
}

Pour

foreach my $f (@files){
    open my $in, '<', $f;
    open my $out, '>', "$f.out";
    while (my $line = <$in>){
        chomp $line;
        $line =~ s/thisgoesout/thisgoesin/gi
        print $out "$line\n";
    }
}

Cela suppose que le motif ne couvre pas plusieurs lignes. Si le motif peut s'étendre sur plusieurs lignes, vous devrez extraire le contenu du fichier. (& "; slurp &" est un terme assez commun de Perl).

Le chomp n'est pas vraiment nécessaire, je viens de me faire piquer par des lignes qui n'ont pas été chomp éditées trop souvent (si vous supprimez le print $out "$line\n";, remplacez print $out $line; par open my $out, '>', "$f.out";).

De même, vous pouvez changer open my $out, '>', undef; en <=> pour ouvrir un fichier temporaire, puis copier ce fichier sur l'original lorsque la substitution est terminée. En fait, et surtout si vous fouillez dans tout le fichier, vous pouvez simplement effectuer la substitution en mémoire puis écrire sur le fichier d'origine. Mais j’ai fait assez d’erreurs en écrivant toujours dans un nouveau fichier et en vérifiant le contenu.

Remarque , j'avais initialement une instruction if dans ce code. C'était probablement le plus faux. Cela n'aurait copié que les lignes correspondant à l'expression régulière & "Thisgoesout &"; (en le remplaçant par & "thisgoesin &"; bien sûr) tout en engloutissant le reste.

Vous pouvez utiliser find:

find . -name '*.{cs,aspx,ascx}' | xargs perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi"

Ceci listera tous les noms de fichiers de manière récursive, puis xargs lira son stdin et exécutera le reste de la ligne de commande avec les noms de fichiers ajoutés à la fin. Une bonne chose à propos de -i est qu’elle exécutera la ligne de commande plusieurs fois si elle est trop longue pour être exécutée en une fois.

Notez que je ne suis pas sûr si <=> comprend parfaitement toutes les méthodes de sélection de fichiers du shell, donc si ce qui précède ne fonctionne pas, essayez peut-être:

find . | grep -E '(cs|aspx|ascx)$' | xargs ...

Lorsque j'utilise des pipelines comme celui-ci, j'aime bien construire la ligne de commande et exécuter chaque composant individuellement avant de continuer, afin de m'assurer que chaque programme obtient l'entrée qu'il souhaite. Vous pouvez donc exécuter la partie sans <=> d'abord la vérifier.

Je viens de penser que même si vous ne l'avez pas dit, vous êtes probablement sous Windows en raison des suffixes de fichier que vous recherchez. Dans ce cas, le pipeline ci-dessus pourrait être exécuté à l'aide de Cygwin. Il est possible d'écrire un script Perl pour faire la même chose, comme vous avez commencé à le faire, mais vous devrez faire la modification sur place vous-même, car vous ne pouvez pas tirer parti du commutateur <=> dans cette situation.

Merci à ephemient de cette question et de cette répondre , j'ai ceci:

use File::Find::Rule;
use strict;

sub ReplaceText {
    my $regex = shift;
    my $replace = shift;

    @ARGV = (File::Find::Rule->file()->name('*.cs','*.aspx','*.ascx')->in('.'));
    $^I = '.bak';
    while (<>) {
        s/$regex/$replace->()/gie;
        print;
    }
}

ReplaceText qr/some(crazy)regexp/, sub { "some $1 text" };

Maintenant, je peux même parcourir une table de hachage contenant regexp = > subs entrées!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top