perl查找并替换删除文件

时间:2016-09-02 15:22:55

标签: regex perl

我是Perl脚本的新手,但我需要在数百个文件中进行大量的正则表达式查找和替换。

我遇到this website,建议使用Perl命令perl -p -i -e 's/oldstring/newstring/g' *获取所有文件,然后perl -p -i -e 's/oldstring/newstring/g' 'find ./ -name *.html\'将其过滤到某些文件。

我的目标是找到所有* .csproj和* .vbproj文件,并将.dll的引用替换为新路径。

这些都是XML文件类型。

我要替换的文字是

<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
</Reference>

<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <Private>True</Private>
  <HintPath>..\..\..\..\ExternalDLLs\log4net.dll</HintPath>
</Reference>

我到目前为止的命令是

perl -p -i -e 's/<Reference Include="log4net, (?:.*?[\t\s\n\r])*?<\/Reference>/<Reference Include="log4net, Version=1\.2\.10\.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL"><SpecificVersion>False<\/SpecificVersion><Private>True<\/Private><HintPath>\.\.\\\.\.\\\.\.\\\.\.\\ExternalDLLs\\log4net\.dll<\/HintPath><\/Reference>/g'  `find . -type f \( -name "*.vbproj" -or -name "*.csproj" \)`

这似乎尝试工作,但它最终删除了我的所有* .vbproj和* .csproj文件。

我无法弄清楚为什么我的脚本会删除文件。

任何帮助?

编辑:它按文件打印出来

Can't do inplace edit on ./Middletier/TDevAccess/AmCad.Components.TDevAccess.csproj: No such file or directory.

编辑2:我在Windows上使用Bash在Ubuntu上是否重要

可能this有关系吗?

2 个答案:

答案 0 :(得分:3)

如果你不小心的话,我建议你以两种不同的方式绊倒自己。

  • 使用正则表达式解析XML是个坏主意。这很麻烦,因为regex不是上下文,而XML就是上下文。
  • Perl有一个非常好的Find模块,这意味着您不需要使用命令版本。

我不知道为什么你遇到问题,但我猜是因为find命令正在生成换行符,而你没有剥离它们?

无论如何,我建议您不要这样做,并使用XML::TwigFile::Find::Rule在perl中完成这项工作。

类似的东西:

#!/usr/bin/perl
use strict;
use warnings;

use File::Find::Rule;
use XML::Twig;

#setup the parser - note, this may reformat (in valid XML sorts of ways).
my $twig = XML::Twig->new(
   pretty_print => 'indented',

   #set a handler for 'Reference' elements - to insert your values.
   twig_handlers => {
      'Reference' => sub {
         $_->insert_new_elt( 'Private' => 'True' );
         $_->insert_new_elt(
            'HintPath' => '..\..\..\..\ExternalDLLs\log4net.dll' );

         #flush is needed to write out the change.
         $_->flush;
      }
   }
);

#use rules to find suitable files to alter.
foreach my $xml_file (
   File::Find::Rule->or(
      File::Find::Rule->name('*.csproj'),
      File::Find::Rule->name('*.vbproj'),
   )->in('.')
  )
{
   print "\nFound: $xml_file\n";

   #do the parse.
   $twig->parsefile_inplace($xml_file);
}

从评论开始 - 如果要扩展以匹配Reference属性,则有两种可能性 - 在特定的 xpath上设置处理程序:

twig_handlers =&gt; {       'Reference [@ Include =“log4net,Version = 1.2.10.0,Culture = neutral,PublicKeyToken = 1b44e1d426115821,processorArchitecture = MSIL”]'=&gt; sub {          $ _-&gt; insert_new_elt('Private'=&gt;'True');          $ _-&GT; insert_new_elt(             'HintPath'=&gt; '........ \ ExternalDLLs \ log4net.dll');

     #flush is needed to write out the change.
     $_->flush;
  }

}

这基于属性内容进行选择(但请记住,上面的内容相当长且令人费解)。

或者 - 处理程序“触发”您遇到的每个引用,因此您可以构建测试。

my $twig = XML::Twig->new(
   pretty_print => 'indented',

   #set a handler for 'Reference' elements - to insert your values.
   twig_handlers => {
      'Reference' => sub {
         #note - instead of 'eq' you can do things like regex tests. 
         if ( $_ -> att('Include') eq "log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL") {
              $_->insert_new_elt( 'Private' => 'True' );
              $_->insert_new_elt( 'HintPath' => '..\..\..\..\ExternalDLLs\log4net.dll' );
         }

         #flush is needed to write out the change.
         $_->flush;
      },
   }
);

答案 1 :(得分:0)

perl -pi逐行处理输入文件。您的替换包含一个正则表达式,尝试匹配跨越多行的某些文本,因此它将无法正常工作。您可以使用-000标志(即perl -000 -pie '.....')激活“slurp”模式,该标志读取内存中的整个文件。当然,您需要确保该目录中没有任何大文件。我不知道为什么文件会被删除,perl -i会重命名原始文件,但这似乎不是问题所在。

另外需要注意的是,如果任何文件的名称包含空格,find ...命令将失败,因此在执行命令之前,您可能会执行IFS=$'\n'之类的操作。