我是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有关系吗?
答案 0 :(得分:3)
如果你不小心的话,我建议你以两种不同的方式绊倒自己。
regex
不是上下文,而XML就是上下文。 Find
模块,这意味着您不需要使用命令版本。 我不知道为什么你遇到问题,但我猜是因为find
命令正在生成换行符,而你没有剥离它们?
无论如何,我建议您不要这样做,并使用XML::Twig
和File::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'
之类的操作。