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

Man*_*anu 45 regex perl substitution

我想做以下事情:

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

$var = "start middle end";
$var =~ s/$find/$replace/;
Run Code Online (Sandbox Code Playgroud)

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

$replace='foo \1 bar';
Run Code Online (Sandbox Code Playgroud)

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


我修复了失踪的's'

yst*_*sth 76

在替换方面,您必须使用$ 1,而不是\ 1.

并且你只能通过使替换成为一个可以提供所需结果的可转换表达式来执行你想要的操作,并告诉s ///使用/ ee修饰符来评估它,如下所示:

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

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

print "var: $var\n";
Run Code Online (Sandbox Code Playgroud)

要查看为什么需要""和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
Run Code Online (Sandbox Code Playgroud)

  • 当然要注意,eval对Web应用程序来说确实很危险,特别是对于无法过滤的任意字符串.请参阅我的评论,为什么我看到了eval方式,然后决定不告诉用户它! (3认同)
  • 双重评估的好例子! (2认同)
  • 这是双重评价的一个非常好的解释:) (2认同)

Ken*_*ric 12

Deparse告诉我们这是正在执行的内容:

$find = 'start (.*) end';
$replace = "foo \cA bar";
$var = 'start middle end';
$var =~ s/$find/$replace/;
Run Code Online (Sandbox Code Playgroud)

然而,

 /$find/foo \1 bar/
Run Code Online (Sandbox Code Playgroud)

被解释为:

$var =~ s/$find/foo $1 bar/;
Run Code Online (Sandbox Code Playgroud)

不幸的是,似乎没有简单的方法可以做到这一点.

你可以用字符串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; 
Run Code Online (Sandbox Code Playgroud)

反对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";
Run Code Online (Sandbox Code Playgroud)

这段代码完全符合你的想法.

如果您的替换字符串在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.
Run Code Online (Sandbox Code Playgroud)

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

  • 不,避免评估通常不是一个好主意.只小心使用它. (3认同)
  • 但是,建议不要让新用户使用eval. (3认同)
  • 这取决于评估数据的来源.避免评估通常是一个好主意. (2认同)

eru*_*orm 7

# perl -de 0
$match="hi(.*)"
$sub='$1'
$res="hi1234"
$res =~ s/$match/$sub/gee
p $res
  1234
Run Code Online (Sandbox Code Playgroud)

但要小心.这会导致eval出现两层e,在正则表达式的末尾各出一层:

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


ike*_*ami 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;
Run Code Online (Sandbox Code Playgroud)

以上是以下各项的缩写:

my $find = 'start (.*) end';
my $replace = 'foo $1 bar';
my $var = "start middle end";
$var =~ s/$find/ eval($replace) /e;
Run Code Online (Sandbox Code Playgroud)

我不喜欢第二个,因为它不会隐藏使用的事实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;
Run Code Online (Sandbox Code Playgroud)

但是如您所见,以上所有内容都允许执行任意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);
Run Code Online (Sandbox Code Playgroud)