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.
Perl: Use s/ (replace) and return new string [duplicate]
Performing a regex search-and-replace is just as easy: $string =~ s/regex/replacement/g; I added a “g” after the last forward slash. The “g” stands for “global”, which tells Perl to replace all matches, and not just the first one.
Substitution Operator or 's' operator in Perl is used to substitute a text of the string with some pattern specified by the user.
Regular Expression (Regex or Regexp or RE) in Perl is a special text string for describing a search pattern within a given text. Regex in Perl is linked to the host language and is not the same as in PHP, Python, etc. Sometimes it is termed as “Perl 5 Compatible Regular Expressions“.
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.)
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;
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. )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With