12

プロジェクトのすべてのソース ファイルの一部のテキストを置換する Perl スクリプトをコーディングしようとしています。私は次のようなものが必要です:

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

しかし、それはディレクトリのすべてのファイルを再帰的に解析します。

スクリプトを開始しました:

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

しかし今、私は立ち往生しています。Perl を使用してすべてのファイルをその場で編集する簡単な方法はありますか?

変更されたすべてのファイルのコピーを保持する必要はないことに注意してください。私はすべてを転覆させました =)

更新: Cygwinでこれを試しました、

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

しかし、引数リストが許容される最大サイズまで爆発したようです。実際、Cygwinで非常に奇妙なエラーが発生しています...

4

6 に答える 6

13

@ARGV使用する前に割り当てると*ARGV(別名、ひし形<>)、$^I/-iはコマンドラインで指定されたものではなく、それらのファイルで機能します。

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

これはあなたが望むことを正確に行うはずです。

パターンが複数行にまたがることができる場合は、Perlが行ごとではなく、一度にファイル全体を操作するように、undef $/;前にを追加します。<>

于 2008-10-29T23:46:25.967 に答える
7

File :: Transaction::AtomicまたはFile::Transactionに興味があるかもしれません

F :: T :: Aの概要は、あなたがやろうとしていることと非常によく似ています。

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

それをFile::Findと組み合わせて、すでに書いたものを見つけてください。

于 2008-10-29T23:19:24.273 に答える
6

Tie :: Fileを使用して、大きなファイルにスケーラブルにアクセスし、その場で変更することができます。マンページ(man 3perl Tie :: File)を参照してください。

于 2008-10-29T23:28:14.053 に答える
4

変化する

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

これは、パターンが複数の行にまたがっていないことを前提としています。パターンが行にまたがる可能性がある場合は、ファイルの内容を丸呑みする必要があります。(「slurp」はかなり一般的なPerl用語です)。

チョップは実際には必要ありません。chomp何度も編集されていない行に噛まれただけです(ドロップした場合はchomp、に変更print $out "$line\n";してくださいprint $out $line;)。

同様に、に変更open my $out, '>', "$f.out";open my $out, '>', undef;て一時ファイルを開き、置換が完了したらそのファイルを元のファイルにコピーして戻すことができます。実際、特にファイル全体を丸呑みする場合は、メモリ内で置換を行ってから、元のファイルに上書きするだけです。しかし、私はそれを行うのに十分な間違いを犯したので、常に新しいファイルに書き込み、内容を確認します。


、私はもともとそのコードにifステートメントを持っていました。それはおそらく間違っていた。それは、正規表現「thisgoesout」(もちろん「thisgoesin」に置き換えます)に一致する行をコピーし、残りを黙ってゴブリングするだけでした。

于 2008-10-29T23:19:08.313 に答える
2

使用できますfind

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

これにより、すべてのファイル名が再帰的にリストxargsされ、標準入力が読み取られ、ファイル名が最後に追加された残りのコマンド ラインが実行されます。良い点の 1 つxargsは、ビルドするコマンド ラインが長くなりすぎて一度に実行できない場合に、コマンド ラインを複数回実行することです。

ファイルを選択するすべてのシェル メソッドを完全に理解しているかどうかはわからないことに注意してください。そのfindため、上記が機能しない場合は、おそらく次を試してください。

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

このようなパイプラインを使用する場合、コマンド ラインを作成し、先に進む前に各部分を個別に実行して、各プログラムが必要な入力を取得していることを確認します。xargsしたがって、最初にチェックせずにパーツを実行できます。

あなたはそうは言っていませんが、探しているファイルのサフィックスが原因で、おそらく Windows を使用していると思いました。その場合、上記のパイプラインは Cygwin を使用して実行できます。あなたが始めたのと同じことを行う Perl スクリプトを書くことは可能ですが-i、その状況ではスイッチを利用できないため、インプレース編集を自分で行う必要があります。

于 2008-10-29T22:35:36.093 に答える
1

この質問とこの答えのエフェミエントのおかげで、私はこれを得ました:

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

これで、regexp=>subs エントリを含むハッシュをループすることさえできます!

于 2008-10-30T23:30:56.573 に答える