Como usar uma variável no lado de substituição do operador de substituição de Perl?

Eu gostaria de fazer o seguinte:

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

Eu esperaria que $ var contivesse “foo middle bar”, mas isso não funciona. Nem faz:

 $replace='foo \1 bar'; 

De alguma forma estou faltando alguma coisa sobre o escape.


Eu consertei o ausente ‘s’

    No lado de substituição, você deve usar $ 1, não \ 1.

    E você só pode fazer o que você quer fazendo substituindo uma expressão que fornece o resultado que você quer e dizendo para que ele / eval seja com o modificador / ee da seguinte forma:

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

    Para ver por que o “” e double / e são necessários, veja o efeito do double eval aqui:

     $ 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 diz que isso é o que está sendo executado:

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

    Contudo,

      /$find/foo \1 bar/ 

    É interpretado como:

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

    Infelizmente parece que não há maneira fácil de fazer isso.

    Você pode fazer isso com uma string eval, mas isso é perigoso.

    A solução mais sensata que funciona para mim foi 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; 

    Uma refutação contra a técnica ee:

    Como eu disse na minha resposta, evito os evals por um motivo.

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

    esse código faz exatamente o que você acha que faz.

    Se sua cadeia de substituição estiver em um aplicativo da web, você acabou de abrir a porta para a execução arbitrária de código.

    Bom trabalho.

    Além disso, não funcionará com as manchas ativadas por essa mesma razão.

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

    No entanto, a técnica mais cuidadosa é sensata, segura e não falha. (Esteja certo de que, a corda que ela emite ainda está contaminada, para que você não perca nenhuma segurança.)

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

    Tenha cuidado, no entanto. Isso faz com que duas camadas de eval ocorrer, uma para cada e no final da regex:

    1. $ sub -> $ 1
    2. $ 1 -> valor final, no exemplo, 1234

    Como outros sugeriram, você poderia usar o seguinte:

     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; 

    O acima é curto para o seguinte:

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

    Eu prefiro o segundo ao primeiro, pois não esconde o fato de que eval(EXPR) é usado. No entanto, ambos os erros de silêncio acima, portanto, o seguinte seria melhor:

     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; 

    Mas como você pode ver, todos os itens acima permitem a execução de código Perl arbitrário. O seguinte seria muito mais 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); 

    Eu sugeriria algo como:

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

    É bastante legível e parece ser seguro. Se várias substituições forem necessárias, é fácil:

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

    Veja este post SO anterior sobre o uso de uma variável no lado de substituição de s/// em Perl. Olhe tanto para a resposta aceita como para a réplica .

    O que você está tentando fazer é possível com a forma s///ee que executa um double eval na string da mão direita. Veja as citações do perlop como operadores para mais exemplos.

    Esteja avisado que há impilcações de segurança de eval e isso não funcionará no modo de contaminação.

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

    Isso me deu o ‘1234’.

    Não tenho certeza do que você está tentando alcançar. Mas talvez você possa usar isto:

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

    Ou seja, deixe o meio sozinho e substitua o início e o fim.