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

发布于 2024-09-10 03:07:55 字数 269 浏览 6 评论 0 原文

我想做以下事情:

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

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

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

$replace = 'foo \1 bar';

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

I would like to do the following:

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

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

I would expect $var to contain "foo middle bar", but it does not work. Neither does:

$replace = 'foo \1 bar';

Somehow I am missing something regarding the escaping.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

回心转意 2024-09-17 03:07:55

在替换方面,您必须使用 $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

尽管正如 ikegami 所说,单个 /e 或双 e 的第一个 /e 并不是真正的 eval();相反,它告诉编译器尽管如此,eval(eval(...)) 仍然说明了为什么您需要做您需要做的事情才能让 /ee 按需要工作。 .)

On the replacement side, you must use $1, not \1.

And you can only do what you want by making replace an evalable expression that gives the result you want and telling s/// to eval it with the /ee modifier like so:

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

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

print "var: $var\n";

To see why the "" and double /e are needed, see the effect of the double eval here:

$ perl
$foo = "middle";
$replace='"foo $foo bar"';
print eval('$replace'), "\n";
print eval(eval('$replace')), "\n";
__END__
"foo $foo bar"
foo middle bar

(Though as ikegami notes, a single /e or the first /e of a double e isn't really an eval(); rather, it tells the compiler that the substitution is code to compile, not a string. Nonetheless, eval(eval(...)) still demonstrates why you need to do what you need to do to get /ee to work as desired.)

随遇而安 2024-09-17 03:07:55

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; 

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

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

Deparse tells us this is what is being executed:

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

However,

 /$find/foo \1 bar/

Is interpreted as :

$var =~ s/$find/foo $1 bar/;

Unfortunately it appears there is no easy way to do this.

You can do it with a string eval, but thats dangerous.

The most sane solution that works for me was this:

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

As I said in my answer, I avoid evals for a reason.

$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";

this code does exactly what you think it does.

If your substitution string is in a web application, you just opened the door to arbitrary code execution.

Good Job.

Also, it WON'T work with taints turned on for this very reason.

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

However, the more careful technique is sane, safe, secure, and doesn't fail taint. ( Be assured tho, the string it emits is still tainted, so you don't lose any security. )

不语却知心 2024-09-17 03:07:55

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

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

As others have suggested, you could use the following:

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;

The above is short for the following:

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

I prefer the second to the first since it doesn't hide the fact that eval(EXPR) is used. However, both of the above silence errors, so the following would be better:

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;

But as you can see, all of the above allow for the execution of arbitrary Perl code. The following would be far safer:

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);
浅沫记忆 2024-09-17 03:07:55
# 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
# perl -de 0
$match="hi(.*)"
$sub='$1'
$res="hi1234"
$res =~ s/$match/$sub/gee
p $res
  1234

Be careful, though. This causes two layers of eval to occur, one for each e at the end of the regex:

  1. $sub --> $1
  2. $1 --> final value, in the example, 1234
花伊自在美 2024-09-17 03:07:55

我会建议这样的内容:

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

它很有可读性,而且似乎很安全。如果需要多次替换,很简单:

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

I would suggest something like:

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

It is quite readable and seems to be safe. If multiple replace is needed, it is easy:

while ($text =~ m{(.*)$find(.*)}){
     $text = $1 . $replace . $2;
}
转身以后 2024-09-17 03:07:55
#!/usr/bin/perl

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

print $res

这让我得到了“1234”。

#!/usr/bin/perl

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

print $res

This got me the '1234'.

一片旧的回忆 2024-09-17 03:07:55

请参阅这个 上一篇关于在 Perl 中的 s/// 替换端使用变量的文章。查看 接受的答案反驳答案。

您尝试做的事情可以使用 s///ee 形式来实现,该形式在右侧字符串上执行双重 eval 。有关更多示例,请参阅 perlop 引用运算符

请注意,eval存在安全隐患,并且这在污点模式下不起作用。

See THIS previous SO post on using a variable on the replacement side of s///in Perl. Look both at the accepted answer and the rebuttal answer.

What you are trying to do is possible with the s///ee form that performs a double eval on the right hand string. See perlop quote like operators for more examples.

Be warned that there are security impilcations of evaland this will not work in taint mode.

尹雨沫 2024-09-17 03:07:55

我没能让最受欢迎的答案发挥作用。

  • 当我的替换字符串包含多个连续的反向引用时,ee 方法会抱怨。
  • 肯特·弗雷德里克的答案仅替换了第一场比赛,我需要我的搜索和替换是全局的。我没有找到一种方法来让它取代所有不会引起其他问题的比赛。例如,我尝试递归运行该方法,直到它不再导致字符串更改,但如果替换字符串包含搜索字符串,则会导致无限循环,而常规全局替换不会这样做。

我尝试使用普通的旧式 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;';

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

I did not manage to make the most popular answers work.

  • The ee method complained when my replacement string contained several consecutive backreferences.
  • Kent Fredric's answer only replaced the first match, and I need my search and replace to be global. I did not figure out a way to make it replace all matches that didn't cause other issues. For example, I tried running the method recursively until it no longer caused the string to change, but that causes an infinite loop if the replacement string contains the search string, whereas a regular global replacement does not do that.

I attempted to come up with a solution of my own using plain old eval:

eval '$var =~ s/' . $find . '/' . $replace . '/gsu;';

Of course, this allows for code injection. But as far as I know, the only way to escape the regex query and inject code is to insert two forward slashes in $find or one in $replace, followed by a semi-colon, after which you can add add code. For example, if I set the variables this way:

my $find = 'foo';
my $replace = 'bar/; print "You\'ve just been hacked!\n"; #';

The evaluated code is this:

$var =~ s/foo/bar/; print "You've just been hacked!\n"; #/gsu;';

So what I do is make sure the strings don't contain any unescaped forward slashes.

First, I copy the strings into dummy strings.

my $findTest = $find;
my $replaceTest = $replace;

Then, I remove all escaped backslashes (backslash pairs) from the dummy strings. This allows me to find forward slashes that are not escaped, without falling into the trap of considering a forward slash escaped if it's preceded by an escaped backslash. For example: \/ contains an escaped forward slash, but \\/ contains a literal forward slash, because the backslash is escaped.

$findTest =~ s/\\\\//gmu;
$replaceTest =~ s/\\\\//gmu;

Now if any forward slash that is not preceded by a backslash remains in the strings, I throw a fatal error, as that would allow the user to insert arbitrary code.

if ($findTest =~ /(?<!\\)\// || $replaceTest =~ /(?<!\\)\//)
{
  print "String must not contain unescaped slashes.\n";
  exit 1;
}

Then I eval.

eval '$var =~ s/' . $find . '/' . $replace . '/gsu;';

I'm not an expert at preventing code injection, but I'm the only one using my script, so I'm content using this solution without fully knowing if it's vulnerable. But as far as I know, it may be, so if anyone knows if there is or isn't any way to inject code into this, please provide your insight in a comment.

苍景流年 2024-09-17 03:07:55

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

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

即只保留中间部分并替换开始和结束。

I'm not certain on what it is you're trying to achieve. But maybe you can use this:

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

I.e. just leave the middle alone and replace the start and end.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文