2016-09-02 108 views
2

我是新手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:IM使用bash在Ubuntu在Windows上,如果该事项

this有关?

回答

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属性,有两家possiblities - 无论是设置在具体的XPath处理程序:

twig_handlers => { '参考[@include =“log4net,Version = 1.2.10。0,Culture = neutral,PublicKeyToken = 1b44e1d426115821,processorArchitecture = MSIL“]'=> sub {_insert_new_elt('Private'=>'True'); $ _-> insert_new_elt( 'HintPath'=> '........ \ 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; 
     }, 
    } 
); 
+0

我还没有运行过这个功能,但是从快速浏览看来,它看起来像是在查找一个XML标签'Reference',并添加了子项'HintPath'和'Private',是否正确?如果是这样,我该如何限制它以查找具有设置为特定值的属性的标签? –

+0

即:具有'Include =“log4net,版本= 1.2.10.0 ...' –

+0

的'Reference'标记足够容易,忍受着我,我会更新这个例子http://xmltwig.org/xmltwig/quick_ref html的 – Sobrique

0

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

另一件需要注意的是,如果任何文件的名称中包含空格,那么find ...命令将会失败,因此在执行该命令之前可能会这样做IFS=$'\n'