如何在Perl正则表达式中跨越换行匹配?

5

我正在尝试通过perl(从shell)进行跨换行匹配。以下是:

(echo a b c d e; echo f g h i j; echo l m n o p) | perl -pe 's/(c.*)/[$1]/'

I get this:

a b [c d e]
f g h i j
l m n o p

这正是我所期望的。但是当我在我的正则表达式末尾加上/s时,会出现这种情况:

a b [c d e
]f g h i j
l m n o p

我期望并希望它输出的是这样的内容:

我所期待的内容是:

a b [c d e
f g h i j
l m n o p
]

我的正则表达式有问题吗?还是我的perl调用标志有问题?

我认为使用/s应该可以完成任务。但是,你也可以使用[\S\s]*代替.* - Rohit Jain
@Rohit Jain:不够充分:他正在使用带有 -p 开关的 Perl,因此需要启用 slurp 模式。 - DavidO
5个回答

12

-p循环逐行处理输入,其中“行”由默认情况下为换行符的输入记录分隔符$/分隔。如果要将所有STDIN读入$_以进行匹配,请使用-0777

$ echo "a b c d e\nf g h i j\nl m n o p" | perl -pe 's/(c.*)/[$1]/s'
a b [c d e
]f g h i j
l m n o p
$ echo "a b c d e\nf g h i j\nl m n o p" | perl -0777pe 's/(c.*)/[$1]/s'
a b [c d e
f g h i j
l m n o p
]

请参阅perlrun中的命令开关,了解这两个标志的信息。同时,-l(连字符加小写字母 L)也会很有用。


将“perl -MO=Deparse -0e1”与“perl -MO=Deparse -0777e1”进行比较。将您的代码更改为使用后者。 - ikegami
那个完美地运行了,而且适用于我的实际用例,而不是这个人为的例子。谢谢 :-) 这让我免去了编写 Ruby 自定义代码的麻烦。 - Sophistifunk

2

有多种方法可以实现它:既然您已经在一次性读取整个文件,我个人会放弃-p修饰符,明确地读取整个输入,然后从那里开始:

echo -e "a b c d e\nf g h i j\nl m n o p" | perl -e '$/ = undef; $_ = <>; s/(c.*)/[$1]/s; print;'

这个解决方案可能会更长一些,但对其他读者(比如三个月后的你 ;-D)来说可能更易懂。


2
问题在于你的单行代码只能逐行运行,而你的正则表达式是正确的:
use strict;
use warnings;
use 5.014;

my $s = qq|a b c d e
f g h i j
l m n o p|;

$s =~ s/(c.*)/[$1]/s;

say $s;

1

实际上,你的一行代码看起来像这样:

while (<>) {

     $ =~ s/(c.*)/[$1]/s;
}

这意味着正则表达式仅适用于您输入的第一行。


1

你一次读取一行,那么你觉得它怎么可能匹配跨越多行的内容呢?

添加-0777来重新定义“行”为“文件”(别忘了添加/s以使.匹配换行符)。

$ (echo a b c d e; echo f g h i j; echo l m n o p) | perl -0777pe's/(c.*)/[$1]/s'
a b [c d e
f g h i j
l m n o p
]

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