¿Cómo usar una variable en el lado de reemplazo del operador de sustitución de Perl?

Me gustaría hacer lo siguiente:

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

Esperaría $ var contener “foo middle bar”, pero no funciona. Tampoco lo hace:

 $replace='foo \1 bar'; 

De alguna manera me falta algo sobre el escape.


Arreglé los “faltantes”

En el lado de reemplazo, debe usar $ 1, no \ 1.

Y solo puede hacer lo que quiera haciendo que replace sea una expresión evalable que le dé el resultado que desea y diciéndole a s /// que lo evalúe con el modificador / ee de la siguiente manera:

 $find="start (.*) end"; $replace='"foo $1 bar"'; $var = "start middle end"; $var =~ s/$find/$replace/ee; print "var: $var\n"; 

Para ver por qué se necesitan “” y double / e, vea el efecto de la doble evaluación aquí:

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

Deparse nos dice que esto es lo que se está ejecutando:

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

Sin embargo,

  /$find/foo \1 bar/ 

Se interpreta como:

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

Desafortunadamente, parece que no hay una manera fácil de hacerlo.

Puedes hacerlo con una evaluación de cadena, pero eso es peligroso.

La solución más sensata que funciona para mí fue esta:

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

Una refutación contra la técnica ee:

Como dije en mi respuesta, evito las evaluaciones por una razón.

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

este código hace exactamente lo que crees que hace.

Si su cadena de sustitución está en una aplicación web, acaba de abrir la puerta a la ejecución de código arbitrario.

Buen trabajo.

Además, NO funcionará con las manchas activadas por esta misma razón.

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

Sin embargo, la técnica más cuidadosa es sensata, segura y no contamina. (Tenga la seguridad de que la cadena que emite todavía está contaminada, por lo que no pierde seguridad).

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

Ten cuidado, sin embargo. Esto provoca que se produzcan dos capas de eval , una para cada e al final de la expresión regular:

  1. $ sub -> $ 1
  2. $ 1 -> valor final, en el ejemplo, 1234

Como otros han sugerido, puede usar lo siguiente:

 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; 

Lo anterior es corto para lo siguiente:

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

Prefiero el segundo al primero, ya que no oculta el hecho de que se usa eval(EXPR) . Sin embargo, los dos errores de silencio anteriores, por lo que lo siguiente sería mejor:

 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; 

Pero como puede ver, todo lo anterior permite la ejecución de código Perl arbitrario. Lo siguiente sería mucho más seguro:

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

Sugeriría algo como:

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

Es bastante legible y parece ser seguro. Si se necesita reemplazo múltiple, es fácil:

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

Vea ESTA publicación anterior SO sobre el uso de una variable en el lado de reemplazo de s/// en Perl. Mire tanto la respuesta aceptada como la respuesta de refutación .

Lo que intenta hacer es posible con el formulario s///ee que realiza una doble eval en la cadena de la derecha. Consulte la cotización perlop como operadores para más ejemplos.

Tenga en cuenta que existen impILcaciones de seguridad de eval y que esto no funcionará en el modo de contaminación.

 #!/usr/bin/perl $sub = "\\1"; $str = "hi1234"; $res = $str; $match = "hi(.*)"; $res =~ s/$match/$1/g; print $res 

Esto me dio el ‘1234’.

No estoy seguro de qué es lo que estás tratando de lograr. Pero tal vez puedas usar esto:

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

Es decir, dejo solo el medio y reemplazo el inicio y el final.