【发布时间】:2013-04-17 13:55:36
【问题描述】:
在我拥有的 perl 程序中,我要求对具有受限权限的文件进行就地编辑:
for my $file (@files) {
`sudo perl -pi -e 's/foo(.?foo2)/bar\$1/m' '$file'`;
}
foo 和 foo2 之间可能有换行符,但文件使用 CRLF 作为换行符。如何将 -pi 命令中的 CRLF 与 s///m 或 s///s 匹配?
【问题讨论】:
标签: perl
在我拥有的 perl 程序中,我要求对具有受限权限的文件进行就地编辑:
for my $file (@files) {
`sudo perl -pi -e 's/foo(.?foo2)/bar\$1/m' '$file'`;
}
foo 和 foo2 之间可能有换行符,但文件使用 CRLF 作为换行符。如何将 -pi 命令中的 CRLF 与 s///m 或 s///s 匹配?
【问题讨论】:
标签: perl
\s* 怎么样?或者,如果您想成为具体的 [\r\n]{0,2}。前者匹配 0 个或多个空格,后者匹配 0 到 2 个换行符或回车符。您还不需要使用-0777 以逐行模式读取文件。
也许比单行运行多个系统命令更好的解决方案是创建一个包含文件列表的脚本文件,例如
system(qw(sudo perl -0777 -pi script.pl), @files);
脚本文件只包含你的替换:
s/foo(?=\s*foo2)/bar/m;
我还将反向引用 $1 替换为前瞻断言。请注意,我建议您在不使用-i 开关或启用-i.bak 备份选项的情况下运行它,直到您知道它可以按预期与您的文件一起使用。
【讨论】:
\R backslash sequence 将匹配任何被 Unicode 视为 linebreak-y 的东西,但对于这个问题可能过于分散。
\s* 应该可以正常工作。
在我回答你的问题之前,让我们先解决两件事。
首先,你有一个无用的反引号。请改用system。
for my $file (@files) {
system("sudo perl ...");
}
其次,您没有正确地将 $file 的内容转换为 shell 文字。考虑一下如果$file 包含单引号会发生什么。修复:
use String::ShellQuote qw( shell_quote );
for my $file (@files) {
system(shell_quote('sudo', 'perl', ..., $file));
}
或者你可以完全避免使用 shell:
for my $file (@files) {
system('sudo', 'perl', ..., $file);
}
关于你的问题。
-p 导致 Perl 用
while (<>) {
... -e ...
} continue {
print;
}
因此,
/foo(\r\nfoo2)/
或者更灵活的
/foo(\s*\nfoo2)/
不可能匹配,因为您还没有阅读foo2。您可以通过首先执行local $/; 来使<> 读取整个文件,这可以通过添加-0777 来完成
$/ = undef;
while (<>) {
...[ code from -e ]...
} continue {
print;
}
所以你需要的是:
for my $file (@files) {
system('sudo', 'perl', '-i', '-0777pe', 's/foo(\s*\nfoo2)/bar$1/', $file);
}
或者你甚至可以避免 for 循环并使用
system('sudo', 'perl', '-i', '-0777pe', 's/foo(\s*\nfoo2)/bar$1/', @files);
【讨论】: