#! /usr/bin/perl
use warnings;
use strict;
my %replace = (
"foo" => "baz",
"bar" => "quux",
);
my $to_replace = qr/@{["(" .
join("|" => map quotemeta($_), keys %replace) .
")"]}/;
while (<DATA>) {
s/$to_replace/$replace{$1}/g;
print;
}
__DATA__
The food is under the bar in the barn.
@{[...]} 位可能看起来很奇怪。在quote and quote-like operators 中插入生成的内容是一种技巧。 join 的结果进入匿名数组引用构造函数 [] 并立即取消引用,感谢 @{}。
如果这一切看起来太古怪了,那就是一样
my $search = join "|" => map quotemeta($_), keys %replace;
my $to_replace = qr/($search)/;
减去临时变量。
注意quotemeta 的使用——感谢 Ivan!——它转义了每对的第一个字符串,因此正则表达式引擎会将它们视为文字字符串。
输出:
bazd在quuxn中的quux下。
元编程——即编写一个程序来编写另一个程序——也很好。开头看起来很熟悉:
#! /usr/bin/perl
use warnings;
use strict;
use File::Compare;
die "Usage: $0 path ..\n" unless @ARGV >= 1;
# stub
my @pairs = (
["foo" => "baz"],
["bar" => "quux"],
['foo$bar' => 'potrzebie\\'],
);
现在我们生成的程序可以完成所有s/// 替换——但is quotemeta on the replacement side a good idea?——
my $code =
"sub { while (<>) { " .
join(" " => map "s/" . quotemeta($_->[0]) .
"/" . quotemeta($_->[1]) .
"/g;",
@pairs) .
"print; } }";
#print $code, "\n";
并用eval编译它:
my $replace = eval $code
or die "$0: eval: $@\n";
要进行替换,我们使用 Perl 的 ready-made in-place editing:
# set up in-place editing
$^I = ".bak";
my @save_argv = @ARGV;
$replace->();
以下是恢复 File::Compare 模块判断为不必要的备份的额外功能:
# in-place editing is conservative: it creates backups
# regardless of whether it modifies the file
foreach my $new (@save_argv) {
my $old = $new . $^I;
if (compare($new, $old) == 0) {
rename $old => $new
or warn "$0: rename $old => $new: $!\n";
}
}