regex - pattern - ¿Cómo usar una variable en el lado de reemplazo del operador de sustitución de Perl?
matching pattern perl (8)
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);
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"
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).
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
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.
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 rebuttal .
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.
# 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:
- $ sub -> $ 1
- $ 1 -> valor final, en el ejemplo, 1234
#!/usr/bin/perl
$sub = "//1";
$str = "hi1234";
$res = $str;
$match = "hi(.*)";
$res =~ s/$match/$1/g;
print $res
Esto me dio el ''1234''.