perl 多行匹配问题

问题描述 投票:0回答:6

我正在尝试使用 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

看来中间步骤应该没有必要。我是不是错过了什么?

regex perl
6个回答
23
投票

你很接近。您需要

-00
-0777
:

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

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 对,
.+
的贪婪匹配将吃掉从第一个 START 到最后一个 STOP 的所有内容。您可以使用非贪婪匹配(尽可能少匹配)与
.+?

如果您想在字符串中的任意位置使用

^
$
锚点作为行边界,那么您还需要
/m
正则表达式修饰符。


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
投票

这是一个不会立即将整个文件读入内存的单行代码:

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

0
投票

-g
-0777
的别名,在我看来更具可读性。

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

https://perldoc.perl.org/perlrun#-g

© www.soinside.com 2019 - 2024. All rights reserved.