Perl多行匹配出现问题

22

我正在尝试使用 Perl 的一行命令来更新跨越多行的代码,但是遇到了一些奇怪的行为。这里是一个简单的文本文件,展示了我看到的问题:

ABCD    START
         STOP    EFGH

我原本以为以下代码可以起作用,但它实际上没有替换任何东西:

perl -pi -e 's/START\s+STOP/REPLACE/s' input.txt

经过一些实验,我发现原始正则表达式中的\s+将匹配换行符,但不会匹配第二行上的任何空格,添加第二个\s+也不起作用。因此,目前我正在使用以下解决方法,即添加一个仅删除换行符的中间正则表达式:

perl -pi -e 's/START\s+/START/s' input.txt

这将创建以下中间文件:

ABCD    START            STOP    EFGH

那么我可以运行原始正则表达式(尽管/s不再需要):

perl -pi -e 's/START\s+STOP/REPLACE/s' input.txt

这将创建最终所需的文件:

ABCD    REPLACE    EFGH

看起来中间步骤是不必要的。我有什么遗漏吗?


你的常见问题在第一句话中得到了回答:“perldoc -q match” --> “我在匹配超过一行时遇到了麻烦。出了什么问题?” - tadmc
2
/s 只影响 . 匹配的内容,因此您不需要使用任何 /s - ysth
5个回答

23

perl -p一次处理一行文件。你的正则表达式是正确的,但它从未与多行字符串匹配。

一个简单的策略,假设文件可以放入内存中,是读取整个文件(不需要使用-p):

$/ = undef;
$file = <>;
$file =~ s/START\s+STOP/REPLACE/sg;
print $file;
注意,我已添加/g修饰符以指定全局替换。
为了简化所有额外的样板代码,您可以使用现有脚本及-0777选项:perl -0777pi -e 's/START\s+STOP/REPLACE/sg'。如果您需要在文件中进行多次替换,则仍然需要添加/g
一个可能会遇到的问题(尽管不是与此正则表达式):如果正则表达式是START.+STOP,文件包含多个START / STOP对,贪婪匹配.+将吃掉从第一个开始到最后一个STOP的所有内容。你可以用非贪婪匹配(尽可能少地匹配)来匹配.+?
如果您想在字符串中的任何位置使用^$锚定行边界,则还需要/m正则表达式修饰符。

3
还找不到-0的任何信息,这个标志是什么意思? - Jonathan Dumaine
这让我疯了!非常感谢 :) - PiersyP

23
你很接近了。你需要使用 -00 或者 -0777 中的一个:
 perl -0777 -pi -e 's/START\s+/START/' input.txt

5
-0777-00的作用是什么?我正在阅读Perl手册,但除了这些数字是八进制(这很明显)之外,我找不到任何信息。谢谢! - Justin Force
3
选项 -0 改变记录分隔符。选项 777 激活" slurp mode ",在此模式下未定义记录分隔符,因此整个文件一次性读取。选项 0 将分隔符更改为空行。 - J. Katzwinkel

6

一个相对简单的一行代码(在内存中读取文件):

perl -pi -e 'BEGIN{undef $/;} s/START\s+STOP/REPLACE/sg;' input.txt

另一种选择(不太简单),不在内存中读取文件:

perl -ni -e '$a.=$_; \
             if ( $a =~ s/START\s+STOP/REPLACE/s ) { print $a; $a=""; } \
             END{$a && print $a}' input.txt

3
perl -MFile::Slurp -e '$content = read_file(shift); $content =~ s/START\s+STOP/REPLACE/s; print $content' input.txt

3
为什么您要让人们使用一个非标准模块来完成一个单一的简单命令行可以完全处理的事情呢? - tchrist

3
这是一个不需要一次性读取整个文件到内存中的一行代码:
perl -i -ne 'if (($x = $last . $_) =~ s/START\n\s*STOP/REPLACE/) \
  { print $x; $last = ""; } else { print $last; $last = $_; } \
  print $last if eof ARGV' input.txt

不错,虽然我认为ARGV没有起到任何作用,可以将其删除。 - Ken Schumack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接