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

问题描述 投票:44回答:8

我想做以下事情:

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

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

我希望$ var包含“foo middle bar”,但它不起作用。也不是:

$replace='foo \1 bar';

不知怎的,我错过了关于逃避的事情。


我修复了失踪的's'

regex perl substitution
8个回答
75
投票

在替换方面,您必须使用$ 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,请在此处查看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(...))仍然说明为什么你需要做你需要做的事情才能让e ee按照你的意愿工作。)


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; 

A rebuttal against the ee technique:

正如我在答案中所说,我出于某种原因避免了逃避。

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

然而,更谨慎的技术是理智,安全,安全,并且不会失败。 (请放心,它发出的字符串仍然受到污染,因此您不会失去任何安全性。)


6
投票
# perl -de 0
$match="hi(.*)"
$sub='$1'
$res="hi1234"
$res =~ s/$match/$sub/gee
p $res
  1234

但要小心。这导致出现两层eval,一个在正则表达式末尾的每个e

  1. $ sub - > $ 1
  2. $ 1 - >最终值,在示例中,1234

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

1
投票

我会建议像:

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

它非常易读,似乎很安全。如果需要多次更换,很容易:

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

1
投票

请参阅THIS之前关于在s///in Perl的替换面上使用变量的SO帖子。看看accepted answerrebuttal答案。

你想要做的是使用s///ee形式,在右手弦上执行双eval。有关更多示例,请参阅perlop quote like operators

请注意,evaland存在安全隐患,这在污点模式下无效。


0
投票
#!/usr/bin/perl

$sub = "\\1";
$str = "hi1234";
$res = $str;
$match = "hi(.*)";
$res =~ s/$match/$1/g;

print $res

这让我得到了'1234'。


-5
投票

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

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

即只留下中间并替换开始和结束。

© www.soinside.com 2019 - 2024. All rights reserved.