tell - 替换perl




有一个简单的方法来执行批量文件文本替换吗? (4)

我一直在试图编写一个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上遇到了很奇怪的错误...


你可以使用find

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

这将递归列出所有文件名,然后xargs将读取它的标准输入并运行命令行的剩余部分,并在末尾添加文件名。 关于xargs一个xargs是,如果它构建的命令行太长而不能一次运行,它会多次运行命令行。

请注意,我不知道是否find完全理解选择文件的所有shell方法,所以如果上述不起作用,那么也许尝试:

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

当使用这样的管道时,我喜欢建立命令行并在继续之前单独运行每个部分,以确保每个程序都获得所需的输入。 所以你可以先运行没有xargs的部分来检查它。

我刚想到,虽然你没有这么说,但你可能是在Windows上,因为你正在寻找文件后缀。 在这种情况下,上面的管道可以使用Cygwin运行。 您可以编写一个Perl脚本来执行相同的操作,但您必须自己进行就地编辑,因为在这种情况下无法利用-i开关。


如果您在使用*ARGV (aka钻石<> )之前分配@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;
}

这应该做你想要的。

如果你的模式可以跨越多行,添加一个undef $/;<>之前,这样Perl就可以在一个文件中而不是一行一行地操作整个文件。


您可能对File :: Transaction :: AtomicFile :: 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 ([email protected]) {
      $ft->revert;
      die "update aborted: [email protected]";
  }
  else {
      $ft->commit;
  }

再加上File :: Find你已经写好了,你应该很好走。


更改

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\n";print $out $line; )。

同样,你可以改变open my $out, '>', "$f.out"; open my $out, '>', undef; 打开临时文件,然后在替换完成时将该文件复制到原始文件上。 事实上,特别是如果你在整个文件中徘徊,你可以简单地在内存中进行替换,然后写在原始文件上。 但是我犯了足够多的错误,我总是写一个新文件,并验证内容。

请注意 ,我原来在该代码中有一个if语句。 这很可能是错误的。 那只会复制一些与正则表达式“thisgoesout”相匹配的行(当然用“thisgoesin”代替它),同时默默地吞噬其余的行。





bulk