58 votes

Comment utiliser une variable dans le côté remplacement de l'opérateur de substitution Perl?

Je voudrais faire ce qui suit:

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

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

Je m'attendrais à ce que $ var contienne "foo middle bar", mais cela ne fonctionne pas. Pas plus:

 $replace='foo \1 bar';
 

D'une certaine manière, il me manque quelque chose concernant l'évasion.


J'ai corrigé le 's' manquant

89voto

ysth Points 54757

Du côté du remplacement, vous devez utiliser $ 1, pas \ 1.

Et vous ne pouvez faire ce que vous voulez qu'en faisant remplacer une expression évaluable qui donne le résultat souhaité et en disant à s /// de l'évaluer avec le modificateur / ee comme ceci:

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

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

print "var: $var\n";
 

Pour voir pourquoi le "" et double / e sont nécessaires, voyez l'effet du double eval ici:

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

13voto

Kent Fredric Points 35592

Deparse nous dit que c'est ce qui est en cours d'exécution:

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

Cependant,

 /$find/foo \1 bar/

Est interprété comme :

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

Malheureusement, il n'existe pas de moyen facile de le faire.

Vous pouvez le faire avec une chaîne eval, mais c'est dangereux.

Le plus sain solution qui fonctionne pour moi est celle-ci:

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

Une réfutation à l'encontre de l'ee de la technique:

Comme je l'ai dit dans ma réponse, j'évite de est évaluée comme pour une raison.

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

ce code fait exactement ce que vous pensez que cela fonctionne.

Si votre chaîne de substitution est dans une application web, vous venez d'ouvrir la porte à l'exécution de code arbitraire.

Du Bon Travail.

Aussi, il ne sera PAS travailler avec de l'atteinte activé pour cette raison.

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

Cependant, le plus prudent technique est sain, sûr, sécuritaire, et n'est pas sans souillure. ( Être assuré tho, la chaîne de caractères qu'elle émet est encore infecté, afin que vous ne perdez pas de sécurité. )

7voto

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

Attention cependant. Cela provoque deux couches de eval , une pour chaque e à la fin de l'expression régulière:

  1. $ sub -> $ 1
  2. 1 $ -> valeur finale, dans l'exemple, 1234

1voto

Pavel Coodan Points 1

Je suggérerais quelque chose comme:

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

Il est tout à fait lisible et semble sûr. Si plusieurs remplacements sont nécessaires, c'est simple:

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

1voto

dawg Points 26051

Voir CE précédent post DONC sur l'utilisation d'une variable sur le remplacement du côté de l' s///en Perl. Vérifier à la fois la accepté de répondre et la réfutation de la réponse.

Ce que vous essayez de faire est possible avec l' s///ee formulaire qui effectue un double - eval sur la main droite de la chaîne. Voir perlop citer comme opérateurs pour plus d'exemples.

Être averti que la sécurité impilcations d' evalet cela ne fonctionne pas dans souillure mode.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X