我正在尝试编写一个简单的perl脚本,将给定的正则表达式应用于文件名等等,而我将正则表达式作为参数传递给脚本时遇到了麻烦。
我希望能做的是这样的事情:
> myscript 's/hi/bye/i' hi.h
bye.h
>
我已经制作了这段代码
#!/utils/bin/perl -w
use strict;
use warnings;
my $n_args = $#ARGV + 1;
my $regex = $ARGV[0];
for(my $i=1; $i<$n_args; $i++) {
my $file = $ARGV[$i];
$file =~ $regex;
print "OUTPUT: $file\n";
}
我不能使用qr,因为显然它不能用于替换正则表达式(虽然我的来源是一个论坛帖子,所以我很高兴被证明是错误的。)
我宁愿避免将两个部分作为单独的字符串传递并在perl脚本中手动执行正则表达式。
是否可以将正则表达式作为这样的参数传递,如果是这样,最好的方法是什么?
答案 0 :(得分:8)
我认为有不止一种方法可以做到这一点。
Ev i al Way:
由于您基本上发送了一个正则表达式,因此可以对其进行求值以获得结果。像这样:
my @args = ('s/hi/bye/', 'hi.h');
my ($regex, @filenames) = @args;
for my $file (@filenames) {
eval("\$file =~ $regex");
print "OUTPUT: $file\n";
}
当然,按照这种方式会让你看到一些非常令人讨厌的惊喜。例如,考虑传递这组参数:
...
my @args = ('s/hi/bye/; print qq{MINE IS AN EVIL LAUGH!\n}', 'hi.h');
...
是的,它会嘲笑你最喜欢 a illy。
安全的方式:
my ($regex_expr, @filenames) = @args;
my ($substr, $replace) = $regex_expr =~ m#^s/((?:[^/]|\\/)+)/((?:[^/]|\\/)+)/#;
for my $file (@filenames) {
$file =~ s/$substr/$replace/;
print "OUTPUT: $file\n";
}
正如您所看到的,我们将给出的表达式解析为两部分,然后使用这些部分构建一个完整的运算符。显然,这种方法不够灵活,但当然,它更安全。
最简单的方法:
my ($search, $replace, @filenames) = @args;
for my $file (@filenames) {
$file =~ s/$search/$replace/;
print "OUTPUT: $file\n";
}
是的,没错 - 根本没有正则表达式解析!这里发生的是我们决定采用两个参数 - “搜索模式”和“替换字符串” - 而不是单个参数。它会使我们的脚本不像前一个那么灵活吗?不,因为我们仍然需要或多或少地定期解析正则表达式。但是现在用户清楚地理解所有给予命令的数据,这通常是一个很大的改进。 )
两个示例中的@args对应于@ARGV数组。
答案 1 :(得分:4)
s/a/b/i
是一个运算符,而不仅仅是一个正则表达式,因此如果您希望正确解释它,则需要使用eval
。
#!/usr/bin/env perl
use warnings;
use strict;
my $regex = shift;
my $sub = eval "sub { \$_[0] =~ $regex; }";
foreach my $file (@ARGV) {
&$sub($file);
print "OUTPUT: $file\n";
}
这里的诀窍是我用这个&#34;代码代替#34;到一个字符串,生成Perl代码,定义一个匿名子程序$_[0] =~ s/a/b/i;
(或你传递它的任何代码),然后使用eval
编译该代码并给我一个代码引用,我可以在循环内调用
$ test.pl 's/foo/bar/' foo nicefood
OUTPUT: bar
OUTPUT: nicebard
$ test.pl 'tr/o/e/' foo nicefood
OUTPUT: fee
OUTPUT: nicefeed
这比在循环中放置eval "\$file =~ $regex;"
更有效,因为它会在每次迭代时进行编译和评估,而不是仅仅预先编译一次。
关于eval
的警告 - 正如raina77ow的回答所解释的那样,您应该避免使用eval
,除非您100%确定始终从受信任的来源获取您的意见...
答案 2 :(得分:2)
s/a/b/i
不是正则表达式。这是一个正则表达式加替换。除非你使用字符串eval,否则这项工作可能非常困难(考虑s{a}<b>e
等等)。
答案 3 :(得分:2)
问题是当你真正需要传递的是参数时,你试图传递一个perl运算符:
myscript hi bye hi.h
在剧本中:
my ($find, $replace, @files) = @ARGV;
...
$file =~ s/$find/$replace/i;
你的代码有点笨拙。这就是你所需要的:
use strict;
use warnings;
my ($find, $replace, @files) = @ARGV;
for my $file (@files) {
$file =~ s/$find/$replace/i;
print "$file\n";
}
请注意,这种方式允许您在正则表达式中使用元字符,例如\w{2}foo?
。这既是好事,也是坏事。要使所有字符按字面意思排列(禁用元字符),您可以使用\Q ... \E
,如下所示:
... s/\Q$find\E/$replace/i;