Come utilizzare una variabile nel lato di sostituzione dell’operatore di sostituzione Perl?

Mi piacerebbe fare quanto segue:

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

Mi aspetterei che $ var contenga “foo middle bar”, ma non funziona. Neanche:

 $replace='foo \1 bar'; 

In qualche modo mi manca qualcosa riguardo alla fuga.


Ho risolto i missing ‘s’

    Sul lato di sostituzione, è necessario utilizzare $ 1, non \ 1.

    E puoi solo fare quello che vuoi, sostituendo un’espressione evalable che dà il risultato che vuoi e dicendo s /// per valutarlo con il modificatore / ee in questo modo:

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

    Per capire perché sono necessari “” e double / e, vedi l’effetto del doppio eval qui:

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

    Deparse ci dice che questo è ciò che viene eseguito:

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

    Però,

      /$find/foo \1 bar/ 

    È interpretato come:

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

    Purtroppo sembra che non ci sia un modo semplice per farlo.

    Puoi farlo con una stringa di valutazione, ma questo è pericoloso.

    La soluzione più sana che funziona per me è questa:

     $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 confutazione contro la tecnica ee:

    Come ho detto nella mia risposta, evito le evals per una ragione.

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

    questo codice fa esattamente quello che pensi che faccia.

    Se la stringa di sostituzione è in un’applicazione Web, hai appena aperto la porta all’esecuzione di codice arbitrario.

    Buon lavoro.

    Inoltre, NON funzionerà con i taints accesi proprio per questo motivo.

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

    Tuttavia, la tecnica più attenta è sana, sicura, sicura e non guasta. (Assicurati, la stringa che emette è ancora macchiata, quindi non perdi alcuna sicurezza.)

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

    Stai attento, però. Ciò causa due livelli di eval , uno per ogni e alla fine della regex:

    1. $ sub -> $ 1
    2. $ 1 -> valore finale, nell’esempio, 1234

    Come altri hanno suggerito, puoi usare quanto segue:

     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; 

    Quanto sopra è breve per quanto segue:

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

    Preferisco il secondo al primo poiché non nasconde il fatto che viene utilizzato eval(EXPR) . Tuttavia, entrambi gli errori di silenzio di cui sopra, quindi il seguente sarebbe meglio:

     my $find = 'start (.*) end'; my $replace = 'foo $1 bar'; my $var = "start middle end"; $var =~ s/$find/ my $r = eval($replace); die [email protected] if [email protected]; $r /e; 

    Ma come puoi vedere, tutto quanto sopra consente l’esecuzione di codice Perl arbitrario. Quanto segue sarebbe molto più sicuro:

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

    Suggerirei qualcosa come:

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

    È abbastanza leggibile e sembra essere sicuro. Se è necessaria la sostituzione multipla, è facile:

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

    Vedi QUESTO post SO precedente sull’uso di una variabile sul lato di sostituzione di s/// in Perl. Guarda sia la risposta accettata che la risposta alla confutazione .

    Quello che stai cercando di fare è ansible con il modulo s///ee che esegue una doppia eval nella stringa di destra. Vedi perlop quote come operatori per altri esempi.

    Essere avvertito che ci sono impilazioni di sicurezza di eval e questo non funzionerà in modalità contaminazione.

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

    Questo mi ha procurato il “1234”.

    Non sono sicuro su cosa stai cercando di raggiungere. Ma forse puoi usare questo:

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

    Cioè, lascia solo la metà e sostituisci l’inizio e la fine.