我想做以下事情:
$find="start (.*) end";
$replace="foo \1 bar";
$var = "start middle end";
$var =~ s/$find/$replace/;
我希望$ var包含“foo middle bar”,但它不起作用。也没有:
$replace='foo \1 bar';
不知何故,我遗漏了有关逃跑的事情。
我修复了缺失的''
答案 0 :(得分:74)
在替换方面,您必须使用$ 1,而不是\ 1.
你只能做一个你想要的东西,通过使一个可用的表达式得到你想要的结果,并告诉s ///用/ ee修饰符来评估它,如下所示:
$find="start (.*) end";
$replace='"foo $1 bar"';
$var = "start middle end";
$var =~ s/$find/$replace/ee;
print "var: $var\n";
要查看为什么需要“”和double / e,请在此处查看双eval的效果:
$ perl
$foo = "middle";
$replace='"foo $foo bar"';
print eval('$replace'), "\n";
print eval(eval('$replace')), "\n";
__END__
"foo $foo bar"
foo middle bar
(虽然正如ikegami所说,单个/ e或双e的第一个/ e实际上并不是eval()
;相反,它告诉编译器替换是编译的代码,而不是字符串尽管如此,eval(eval(...))
仍然证明了为什么你需要做你需要做的事情才能让e ee按照需要工作。)
答案 1 :(得分:12)
Deparse告诉我们这是正在执行的内容:
$find = 'start (.*) end';
$replace = "foo \cA bar";
$var = 'start middle end';
$var =~ s/$find/$replace/;
然而,
/$find/foo \1 bar/
被解释为:
$var =~ s/$find/foo $1 bar/;
不幸的是,似乎没有简单的方法可以做到这一点。
你可以用字符串eval来做,但这很危险。
最适合我的解决方案是:
$find = "start (.*) end";
$replace = 'foo \1 bar';
$var = "start middle end";
sub repl {
my $find = shift;
my $replace = shift;
my $var = shift;
# Capture first
my @items = ( $var =~ $find );
$var =~ s/$find/$replace/;
for( reverse 0 .. $#items ){
my $n = $_ + 1;
# Many More Rules can go here, ie: \g matchers and \{ }
$var =~ s/\\$n/${items[$_]}/g ;
$var =~ s/\$$n/${items[$_]}/g ;
}
return $var;
}
print repl $find, $replace, $var;
正如我在答案中所说,我出于某种原因避免了逃避。
$find="start (.*) end";
$replace='do{ print "I am a dirty little hacker" while 1; "foo $1 bar" }';
$var = "start middle end";
$var =~ s/$find/$replace/ee;
print "var: $var\n";
此代码完全符合您的想法。
如果您的替换字符串在Web应用程序中,您只需打开任意代码执行的大门。
干得好。
此外,由于这个原因,不会使用taints。
$find="start (.*) end";
$replace='"' . $ARGV[0] . '"';
$var = "start middle end";
$var =~ s/$find/$replace/ee;
print "var: $var\n"
$ perl /tmp/re.pl 'foo $1 bar'
var: foo middle bar
$ perl -T /tmp/re.pl 'foo $1 bar'
Insecure dependency in eval while running with -T switch at /tmp/re.pl line 10.
然而,更谨慎的技术是理智,安全,安全,和不会失败。 (请放心,它发出的字符串仍然受到污染,所以你不会失去任何安全性。)
答案 2 :(得分:6)
# perl -de 0
$match="hi(.*)"
$sub='$1'
$res="hi1234"
$res =~ s/$match/$sub/gee
p $res
1234
但是,要小心。这会导致出现两层eval
,在正则表达式结尾处为每个e
创建一层:
答案 3 :(得分:5)
正如其他人所建议的那样,您可以使用以下内容:
my $find = 'start (.*) end';
my $replace = 'foo $1 bar'; # 'foo \1 bar' is an error.
my $var = "start middle end";
$var =~ s/$find/$replace/ee;
上述内容简称如下:
my $find = 'start (.*) end';
my $replace = 'foo $1 bar';
my $var = "start middle end";
$var =~ s/$find/ eval($replace) /e;
我更喜欢第二个到第一个,因为它没有隐藏使用eval(EXPR)
的事实。但是,上述两种沉默错误,所以以下情况会更好:
my $find = 'start (.*) end';
my $replace = 'foo $1 bar';
my $var = "start middle end";
$var =~ s/$find/ my $r = eval($replace); die $@ if $@; $r /e;
但正如您所看到的,上述所有内容都允许执行任意Perl代码。以下将更安全:
use String::Substitution qw( sub_modify );
my $find = 'start (.*) end';
my $replace = 'foo $1 bar';
my $var = "start middle end";
sub_modify($var, $find, $replace);
答案 4 :(得分:1)
我会建议像:
$text =~ m{(.*)$find(.*)};
$text = $1 . $replace . $2;
它非常易读,似乎很安全。如果需要多次更换,很容易:
while ($text =~ m{(.*)$find(.*)}){
$text = $1 . $replace . $2;
}
答案 5 :(得分:1)
请参阅THIS上一篇关于在Perl中s///
的替换面上使用变量的SO帖子。查看accepted answer和rebuttal答案。
使用s///ee
表单在右侧字符串上执行双eval
表示您可以尝试执行的操作。有关更多示例,请参阅perlop quote like operators。
警告eval
存在安全隐患,这在污点模式下无效。
答案 6 :(得分:0)
#!/usr/bin/perl
$sub = "\\1";
$str = "hi1234";
$res = $str;
$match = "hi(.*)";
$res =~ s/$match/$1/g;
print $res
这让我得到了'1234'。
答案 7 :(得分:0)
我没有设法使最受欢迎的答案起作用。
我尝试使用简单的旧eval提出自己的解决方案:
eval '$var =~ s/' . $find . '/' . $replace . '/gsu;';
当然,这允许代码注入。但是据我所知,逃避正则表达式查询和注入代码的唯一方法是在$ find中插入两个正斜杠,或在$ replace中插入一个正斜杠,后跟一个分号,之后可以添加添加代码。例如,如果我这样设置变量:
my $find = 'foo';
my $replace = 'bar/; print "You\'ve just been hacked!\n"; #';
评估的代码是这样的:
$var =~ s/foo/bar/; print "You've just been hacked!\n"; #/gsu;';
所以我要做的是确保字符串不包含任何未转义的正斜杠。
首先,我将字符串复制到虚拟字符串中。
my $findTest = $find;
my $replaceTest = $replace;
然后,我从虚拟字符串中删除所有转义的反斜杠(反斜杠对)。这使我能够找到无法转义的正斜杠,而不会陷入考虑在正斜杠转义之前是转义的情况的陷阱。例如:\/
包含一个转义的正斜杠,而\\/
包含一个文字正斜杠,因为反斜杠已被转义。
$findTest =~ s/\\\\//gmu;
$replaceTest =~ s/\\\\//gmu;
现在,如果在字符串中没有任何没有反斜杠的正斜杠,我将引发致命错误,因为这将允许用户插入任意代码。
if ($findTest =~ /(?<!\\)\// || $replaceTest =~ /(?<!\\)\//)
{
print "String must not contain unescaped slashes.\n";
exit 1;
}
然后我评估。
eval '$var =~ s/' . $find . '/' . $replace . '/gsu;';
我不是防止代码注入的专家,但是我是唯一使用脚本的人,因此我很满意使用此解决方案,而完全不知道它是否容易受到攻击。但是据我所知,可能是这样,所以如果有人知道是否有办法向其中注入代码,请在注释中提供您的见识。
答案 8 :(得分:-5)
我不确定你想要实现的是什么。但也许你可以使用它:
$var =~ s/^start/foo/;
$var =~ s/end$/bar/;
即。只留下中间并替换开始和结束。