我有这个字符串如下(这是test1.txt文件的内容):
one
1
</>
two
2
</>
我希望它成为一个像这样的新字符串:
one
1
</>
1
one
</>
two
2
</>
2
two
</>
我使用以下perl one liner来做到这一点。
perl -pi.bak -e 's#((.*)\n(.*)\n<\/>)#$1\n$3\n$2\n<\/>#g' "test1.txt"
但它对test1.txt文件没有任何作用。
更新:我喜欢这三个答案。它们都提供了一些非常有用的信息。在这种情况下,我不确定接受哪个答案......
您正在一次读取一行并与该行匹配,因此您的模式可能无法匹配。
简单的解决方案是使用-0777
(将$/
设置为undef
)将整个文件作为一行读取。
perl -i.bak -0777pe's#((.*)\n(.*)\n<\/>)#$1\n$3\n$2\n<\/>#g' test1.txt
您的一个班轮的剖面:
$ perl -MO=Deparse -pi.bak -e 's#((.*)\n(.*)\n<\/>)#$1\n$3\n$2\n<\/>#g' test.txt
BEGIN { $^I = ".bak"; }
LINE: while (defined($_ = readline ARGV)) {
s[((.*)\n(.*)\n<\/>)][$1\n$3\n$2\n</>]g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
即你的处理循环是基于行的,而你的正则表达式想要匹配多行。
注意:我的解决方案使用更通用的过滤方法STDIN到STDOUT,而不是-i.bak
。
您要么必须将文件篡改到内存中,然后应用替换...
#!/usr/bin/perl
use warnings;
use strict;
use open qw(:encoding(UTF-8) :std);
my $input;
{
local $/;
$input = <STDIN>;
}
$input =~ s,((.*)\n(.*)\n<\/>),$1\n$3\n$2\n<\/>,g;
print $input;
exit 0;
...或在标量上下文中使用双稳态范围运算符的区段检测:
#!/usr/bin/perl
use warnings;
use strict;
use open qw(:encoding(UTF-8) :std);
my @section;
while (<STDIN>) {
if (/^\w+$/../^<\/>$/) {
push(@section, $_);
}
print;
# End of section reached
if (/^<\/>$/) {
# swivel lines around for desired output result...
print @section[1, 0, 2];
@section = ();
}
}
exit 0;
哪种方法更合适取决于您的实际输入文件或其他处理要求。
测试运行:
$ perl dummy.pl <dummy.txt
one
1
</>
1
one
</>
two
2
</>
2
two
</>
更新如果“无重定向”是绝对要求,您可以用<STDIN>
替换<>
来处理命令行上的文件,即
my $input = <>;
要么
while (<>) {
并在命令行上:
$ perl -i.bak dummy.pl test1.txt
-p
选项以每行为基础将输入分配给$_
变量,因此匹配多行的正则表达式不会找到匹配项。在尝试应用正则表达式之前,您应该阅读整个文件:
perl -i.bak -e 'undef $/;$_=<>;s#((.*)\n(.*)\n</>)#$1\n$3\n$2\n</>#g;print' "test1.txt"
在命令行中运行示例:
# perl -e 'undef $/;$_=<>;s#((.*)\n(.*)\n</>)#$1\n$3\n$2\n</>#g;print'<<EOF
> one
> 1
> </>
> two
> 2
> </>
> EOF
one
1
</>
1
one
</>
two
2
</>
2
two
</>