代码之家  ›  专栏  ›  技术社区  ›  Manu JCasso

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

  •  42
  • Manu JCasso  · 技术社区  · 16 年前

    我想做以下工作:

    $find = "start (.*) end";
    $replace = "foo \1 bar";
    
    $var = "start middle end";
    $var =~ s/$find/$replace/;
    

    我希望$var包含“foo中间条”,但它不起作用。也没有:

    $replace = 'foo \1 bar';
    

    不知怎么的,我错过了一些关于逃跑的事情。

    8 回复  |  直到 4 年前
        1
  •  84
  •   ysth    6 年前

    您只能通过使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
    

    (尽管如池上所说,单/e或双e中的第一个/e并不是真正的 eval() ; 相反,它告诉编译器替换是要编译的代码,而不是字符串。尽管如此 eval(eval(...)) 仍然演示了为什么您需要做您需要做的事情以使/ee按预期工作。)

        2
  •  13
  •   Kent Fredric    16 年前

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

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

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

    $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应用程序中,那么您就打开了执行任意代码的大门。

    干得好

    还有, 不会

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

    然而,更仔细的技术是理智的,安全的,安全的, 没有失败的污点。(请确保,它发出的字符串仍然受到污染,因此不会丢失任何安全性。)

        3
  •  7
  •   eruciform    15 年前
    # perl -de 0
    $match="hi(.*)"
    $sub='$1'
    $res="hi1234"
    $res =~ s/$match/$sub/gee
    p $res
      1234
    

    不过要小心。这会导致两层 eval 发生,一个一个 e

    1. $1-->最终值,在示例中为1234
        4
  •  7
  •   ikegami Gilles Quénot    9 年前

    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;
    

    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);
    
        5
  •  1
  •   sth    15 年前

    我想提出如下建议:

    $text =~ m{(.*)$find(.*)};
    $text = $1 . $replace . $2;
    

    while ($text =~ m{(.*)$find(.*)}){
         $text = $1 . $replace . $2;
    }
    
        6
  •  1
  •   rmk    15 年前
    #!/usr/bin/perl
    
    $sub = "\\1";
    $str = "hi1234";
    $res = $str;
    $match = "hi(.*)";
    $res =~ s/$match/$1/g;
    
    print $res
    

    这给了我“1234”。

        7
  •  1
  •   Community CDub    8 年前

    看见 THIS s/// 在Perl中。看看这两个 accepted answer rebuttal

    您试图做的事情是有可能的 s///ee eval 在右手边的绳子上。看见 perlop quote like operators 更多示例。

    请注意,存在安全问题 这在污染模式下不起作用。

        8
  •  1
  •   Quote    5 年前

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

    • 当替换字符串包含几个连续的反向引用时,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;
    }
    

    然后我评估。

    评估“$var=~s/”$找到“/”$替换。”/gsu;
    

    我不是防止代码注入的专家,但我是唯一一个使用我的脚本的人,所以我满足于使用这个解决方案,而不完全知道它是否易受攻击。但据我所知,可能是这样的,所以如果有人知道是否有任何方法可以将代码注入其中,请在评论中提供您的见解。

        9
  •  -6
  •   PEZ    16 年前

    我不确定你想达到什么目的。但也许你可以用这个:

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

    也就是说,只需保留中间部分,并替换起点和终点。