How to use a variable in the replacement side of the Perl substitution operator?
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;
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. )
# 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:
- $sub --> $1
- $1 --> final value, in the example, 1234