Frage

Ich habe versucht, einen Perl-Skript zu codieren, einen Text auf allen Quelldateien von meinem Projekt zu ersetzen. Ich bin in der Notwendigkeit etwas wie:

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

Aber das parst alle die Dateien eines Verzeichnisses rekursiv .

Ich habe gerade angefangen, ein Skript:

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
    }
}

Aber jetzt bin ich stecken. Gibt es eine einfache Möglichkeit, alle Dateien an ihrem Platz mit Perl?

bearbeiten

Bitte beachten Sie, dass ich nicht brauchen eine Kopie jeder geänderten Datei zu halten; Ich bin haben ‚em all subversioned =)

Aktualisieren : Ich habe versucht, dies auf Cygwin ,

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

Aber es sieht aus wie meine Argumente Liste auf die maximal zulässige Größe explodierte. In der Tat, ich bin immer sehr seltsame Fehler auf Cygwin ...

War es hilfreich?

Lösung

Wenn Sie @ARGV zuweisen, bevor *ARGV mit (auch bekannt als der Diamant <>), $^I / -i auf diesen Dateien arbeiten, anstatt dessen, was auf der Kommandozeile angegeben wurde.

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;
}

Dies sollte genau das tun, was Sie wollen.

Wenn Ihr Muster über mehrere Zeilen erstrecken können, fügen Sie in einem undef $/; vor dem <> so dass Perl zu einer Zeit auf eine ganze Datei arbeitet statt Zeile-für-Zeile.

Andere Tipps

Sie interessiert sein an File :: Transaktion :: Atomic oder File :: Transaktion

Die Synopse für F :: T :: A sieht sehr ähnlich mit dem, was Sie zu tun versuchen:

  # 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;
  }

Paar, das mit dem File :: Finden Sie bereits geschrieben haben, und Sie sollten gut gehen.

Sie können die Bindung verwenden :: Dateizugriff große Dateien skalierbar und sie an Ort und Stelle zu ändern. Siehe die Manpage (man 3perl Tie :: File).

Ändern

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

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";
    }
}

Dies setzt voraus, dass das Muster nicht mehr Zeilen nicht überspannen. Wenn die Musterlinien erstrecken könnte, müssen Sie in den Dateiinhalt schlürfen. ( "Schlürfen" ist ein ziemlich häufig Perl Begriff).

Die chomp ist eigentlich nicht nötig, habe ich nur durch Linien gebissen worden, die nicht einmal zu oft chomped wurden (wenn Sie die chomp fallen, ändern print $out "$line\n"; print $out $line;).

Ebenso können Sie open my $out, '>', "$f.out"; ändern open my $out, '>', undef; eine temporäre Datei zu öffnen, und kopieren Sie dann die Datei der ursprünglichen zurück über, wenn der Wechsel getan. In der Tat, und vor allem, wenn Sie in der gesamten Datei schlürfen, können Sie einfach die Substitution in Erinnerung machen und dann die Originaldatei überschreiben. Aber ich habe genug Fehler gemacht, das zu tun, die ich immer in eine neue Datei schreiben, und überprüfen Sie die Inhalte.


Hinweis , ich hatte ursprünglich eine if-Anweisung in diesem Code. Das war wahrscheinlich falsch. Das wäre nur über Leitungen kopiert hat, den regulären Ausdruck „thisgoesout“ abgestimmt (mit „thisgoesin“ natürlich ersetzt), während leise den Rest verschlang.

Sie könnten find verwenden:

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

Diese listet alle Dateinamen rekursiv, dann wird xargs seine stdin lesen und den Rest der Befehlszeile mit den Dateinamen am Ende angehängt laufen. Eine nette Sache über xargs ist es die Befehlszeile mehr als einmal ausgeführt werden, wenn die Befehlszeile wird es zu lang baut auf einmal ausgeführt werden.

Beachten Sie, dass ich nicht sicher bin, ob find alle Shell-Methoden zur Auswahl von Dateien vollständig versteht, so dass, wenn die oben nicht funktioniert, dann vielleicht versuchen:

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

Wenn Pipelines wie folgt aus, ich mag die Befehlszeile für den Aufbau und die einzelnen Teile, bevor Sie fortfahren einzeln ausführen, um sicherzustellen, dass jedes Programm wird immer die Eingabe es will. So können Sie das Teil ohne xargs laufen konnte es zuerst überprüfen.

Es kam nur zu mir, dass Sie zwar nicht so sagten, du bist wahrscheinlich auf Windows aufgrund der Dateiendung, die Sie suchen. In diesem Fall könnte die obige Pipeline mit Cygwin ausgeführt werden soll. Es ist möglich, einen Perl-Skript zu schreiben, das Gleiche zu tun, wie Sie tun gestartet, aber Sie werden die an Ort und Stelle zu tun haben, die Bearbeitung selbst, weil Sie nicht die Vorteile des -i Schalters in dieser Situation nehmen.

Dank dieser Frage ephemient und auf diese beantworten , ich habe dies:

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" };

Jetzt kann ich sogar eine Schleife durch einen Hash enthält regexp => U-Boote Einträge!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top