Regex 如何在Perl替换运算符的替换端使用变量?

Regex 如何在Perl替换运算符的替换端使用变量?,regex,perl,substitution,Regex,Perl,Substitution,我想做以下工作: $find = "start (.*) end"; $replace = "foo \1 bar"; $var = "start middle end"; $var =~ s/$find/$replace/; my $find = 'start (.*) end'; my $replace = 'foo $1 bar'; # 'foo \1 bar' is an error. my $var = "start

我想做以下工作:

$find = "start (.*) end";
$replace = "foo \1 bar";

$var = "start middle end";
$var =~ s/$find/$replace/;
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;
我希望$var包含“foo中间条”,但它不起作用。也没有:

$replace = 'foo \1 bar';

不知何故,我遗漏了一些关于逃跑的事情。

德帕尔斯告诉我们,这就是正在执行的:

$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/;
不幸的是,似乎没有简单的方法可以做到这一点

你可以用字符串求值,但那很危险

对我来说,最明智的解决方案是:

$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; 
对ee技术的反驳: 正如我在回答中所说,我避免评估是有原因的

$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.

然而,更仔细的技术是理智的、安全的、安全的,并且不会失败。(请确保,它发出的字符串仍然被污染,因此您不会失去任何安全性。)

在替换端,您必须使用$1,而不是\1

您只能通过使replace成为一个可求值表达式来执行您想要的操作,该表达式给出您想要的结果,并告诉s///使用/ee修饰符求值,如下所示:

$find="start (.*) end";
$replace='"foo $1 bar"';

$var = "start middle end";
$var =~ s/$find/$replace/ee;

print "var: $var\n";
要了解为什么需要“”和double/e,请参见此处的double 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(…)
仍然说明了为什么您需要做您需要做的事情才能让/ee按预期工作。)

我不确定你想要实现什么目标。但也许你可以用这个:

$var =~ s/^start/foo/;
$var =~ s/end$/bar/;

也就是说,只需保留中间部分,并替换开头和结尾。

我建议如下:

$text =~ m{(.*)$find(.*)};
$text = $1 . $replace . $2;
它可读性很强,似乎很安全。如果需要多次更换,则很容易:

while ($text =~ m{(.*)$find(.*)}){
     $text = $1 . $replace . $2;
}
这给了我“1234”

# perl -de 0
$match="hi(.*)"
$sub='$1'
$res="hi1234"
$res =~ s/$match/$sub/gee
p $res
  1234
不过要小心。这导致出现两层
eval
,每个
e
在正则表达式的末尾有一层:

  • $sub-->$1
  • $1-->最终值,在本例中为1234
  • 请参阅上一篇关于在Perl中使用
    s//
    替换端的变量的SO帖子。看看答案和答案

    您可以使用在右侧字符串上执行双重
    eval
    s///ee
    表单来尝试执行此操作。有关更多示例,请参见


    请注意,存在
    eval
    的安全导入,这在污染模式下不起作用

    正如其他人所建议的,您可以使用以下方法:

    $find = "start (.*) end";
    $replace = "foo \1 bar";
    
    $var = "start middle end";
    $var =~ s/$find/$replace/;
    
    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;
    
    以上是以下内容的简称:

    $find = "start (.*) end";
    $replace = "foo \1 bar";
    
    $var = "start middle end";
    $var =~ s/$find/$replace/;
    
    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;
    
    我更喜欢第二个而不是第一个,因为它不会隐藏使用的事实。但是,上述两种沉默都存在错误,因此以下做法更好:

    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);
    

    我没有设法使最流行的答案起作用

    • 当替换字符串包含几个连续的反向引用时,ee方法发出了抱怨
    • 肯特·弗雷德里克的回答只替换了第一场比赛,我需要我的搜索和替换是全球性的。我没有想出一个办法,让它取代所有匹配,没有造成其他问题。例如,我尝试递归地运行该方法,直到它不再导致字符串更改,但如果替换字符串包含搜索字符串,则会导致无限循环,而常规全局替换不会这样做
    我尝试使用简单的旧评估方法来提出自己的解决方案:

    eval '$var =~ s/' . $find . '/' . $replace . '/gsu;';
    
    当然,这允许代码注入。但据我所知,跳出regex查询并注入代码的唯一方法是在$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对于web应用程序来说是非常危险的,特别是对于无法过滤的任意字符串。请查看我的评论,了解为什么我看到了eval方法,然后决定不告诉用户@肯特·弗雷德里克:是的,如果$foo或$replace来自用户输入,那绝对是有危险的,但从问题来看,这似乎不太可能。而且(正如我看到你指出的)污染模式将阻止未录制的$replace被使用$替换=移位;@ARGV的s/$find/$replace/e有一些变化:分配给
    $replace
    s/$find/'“$replace”/ee
    时引用(追加或sprintf)和fe