[英]How can I disable variable interpolation with the Perl substitution operator?
[英]How can I use a variable in the replacement side of the Perl substitution operator?
我想做以下事情:
$find = "start (.*) end";
$replace = "foo \1 bar";
$var = "start middle end";
$var =~ s/$find/$replace/;
我希望 $var 包含“foo middle bar”,但它不起作用。 也没有:
$replace = 'foo \1 bar';
不知何故,我遗漏了一些关于逃避的东西。
在替换方面,您必须使用 $1,而不是 \\1。
并且您只能通过替换一个给出您想要的结果的可评估表达式并告诉 s/// 使用 /ee 修饰符对其进行评估来做您想做的事情,如下所示:
$find="start (.*) end";
$replace='"foo $1 bar"';
$var = "start middle end";
$var =~ s/$find/$replace/ee;
print "var: $var\n";
要了解为什么需要 "" 和双 /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
(尽管池上指出,单个 /e 或双 e 的第一个 /e 并不是真正的eval()
;相反,它告诉编译器替换是要编译的代码,而不是字符串。尽管如此, eval(eval(...))
仍然展示了为什么你需要做你需要做的事情来让 /ee 按需要工作。)
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;
正如我在回答中所说,我避免 evals 是有原因的。
$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 应用程序中,则您只是打开了执行任意代码的大门。
干得好。
此外,由于这个原因,它不会在打开污点的情况下工作。
$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.
然而,更谨慎的技术是理智的、安全的、可靠的,并且不会失败。 (请放心,它发出的字符串仍然受到污染,因此您不会失去任何安全性。)
正如其他人所建议的那样,您可以使用以下内容:
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);
# perl -de 0
$match="hi(.*)"
$sub='$1'
$res="hi1234"
$res =~ s/$match/$sub/gee
p $res
1234
不过要小心。 这会导致两层eval
发生,正则表达式末尾的每个e
一层:
我会建议这样的:
$text =~ m{(.*)$find(.*)};
$text = $1 . $replace . $2;
它的可读性很强,而且似乎很安全。 如果需要多次更换,很容易:
while ($text =~ m{(.*)$find(.*)}){
$text = $1 . $replace . $2;
}
#!/usr/bin/perl
$sub = "\\1";
$str = "hi1234";
$res = $str;
$match = "hi(.*)";
$res =~ s/$match/$1/g;
print $res
这让我得到了“1234”。
请参阅这篇关于在 Perl 中s///
的替换端使用变量的上一篇 SO 文章。 看看接受的答案和反驳的答案。
您正在尝试使用s///ee
形式对右侧字符串执行双eval
是可能的。 有关更多示例,请参阅perlop quote like operators 。
请注意, eval
存在安全隐患,这在污点模式下不起作用。
我没有设法使最受欢迎的答案起作用。
我尝试使用普通的旧 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;';
我不是防止代码注入的专家,但我是唯一一个使用我的脚本的人,所以我很满意使用这个解决方案,但不完全知道它是否容易受到攻击。 但据我所知,可能是这样,所以如果有人知道是否有任何方法可以将代码注入其中,请在评论中提供您的见解。
我不确定你想要达到什么目的。 但也许你可以使用这个:
$var =~ s/^start/foo/;
$var =~ s/end$/bar/;
即只保留中间部分并替换开始和结束。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.