webentwicklung-frage-antwort-db.com.de

Wie verwende ich eine Variable auf der Ersetzungsseite des Perl-Substitutionsoperators?

Ich möchte folgendes tun:

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

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

Ich würde erwarten, dass $ var "foo middle bar" enthält, aber es funktioniert nicht. Auch nicht:

$replace='foo \1 bar';

Irgendwie vermisse ich etwas bezüglich der Flucht.


Ich habe die fehlenden "s" behoben

42
Manu

Auf der Ersatzseite müssen Sie $ 1 und nicht\1 verwenden.

Und Sie können nur tun, was Sie wollen, indem Sie einen auswertbaren Ausdruck ersetzen, der das gewünschte Ergebnis liefert, und s /// sagen, dass es mit dem Modifizierer/ee ausgewertet werden soll:

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

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

print "var: $var\n";

Um herauszufinden, warum das "" und das doppelte/e benötigt werden, sehen Sie die Wirkung der doppelten Auswertung hier:

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

Deparse sagt uns, dass dies ausgeführt wird: 

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

Jedoch, 

 /$find/foo \1 bar/

Wird interpretiert als:

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

Leider scheint es keinen einfachen Weg zu geben. 

Sie können es mit einem String-Eval tun, aber das ist gefährlich. 

Die vernünftigste Lösung, die für mich funktioniert, war folgende: 

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

Eine Widerlegung gegen die Ee-Technik:

Wie ich schon in meiner Antwort gesagt habe, vermeide ich aus einem bestimmten Grund die Bewertung. 

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

dieser Code macht genau das, was Sie denken. 

Wenn sich Ihre Ersetzungszeichenfolge in einer Webanwendung befindet, haben Sie gerade die Tür zur Ausführung beliebigen Codes geöffnet. 

Gut gemacht. 

Außerdem ist es WON'T Arbeit mit Taints, die aus diesem Grund eingeschaltet sind.

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

Die vorsichtige Technik ist jedoch vernünftig, sicher und sicher, und versagt nicht. (Seien Sie versichert, die von ihr ausgegebene Zeichenfolge ist immer noch verunreinigt, so dass Sie keine Sicherheit verlieren.)

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

Sei aber vorsichtig. Dies führt dazu, dass zwei Schichten von eval auftreten, eine für jede e am Ende der Regex:

  1. $ sub -> $ 1
  2. $ 1 -> Endwert, im Beispiel 1234
6
eruciform

Wie von anderen vorgeschlagen, können Sie Folgendes verwenden:

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;

Das Obige ist kurz für Folgendes:

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

Ich bevorzuge das zweite dem ersten, da dadurch nicht verborgen wird, dass eval(EXPR) verwendet wird. Allerdings sind beide der oben genannten Schweigefehler, daher wäre Folgendes besser:

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;

Wie Sie jedoch sehen können, ermöglichen alle oben genannten Programme die Ausführung von beliebigem Perl-Code. Folgendes wäre weitaus sicherer:

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

Siehe THIS previous SO post zur Verwendung einer Variablen auf der Ersetzungsseite von s///in Perl. Schauen Sie sich sowohl die akzeptierte Antwort als auch die Widerlegung Antwort an. 

Was Sie versuchen, ist mit dem Formular s///ee möglich, das ein doppeltes eval auf der rechten Zeichenfolge durchführt. Siehe Perlop-Zitat wie Operatoren für weitere Beispiele. 

Seien Sie gewarnt, dass eval Sicherheitsbeeinträchtigungen enthält, und dies wird im Verunreinigungsmodus nicht funktionieren. 

1
dawg

Ich würde so etwas vorschlagen:

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

Es ist gut lesbar und scheint sicher zu sein. Wenn mehrere Ersetzungen erforderlich sind, ist dies einfach:

while ($text =~ m{(.*)$find(.*)}){
     $text = $1 . $replace . $2;
}
1
Pavel Coodan
#!/usr/bin/Perl

$sub = "\\1";
$str = "hi1234";
$res = $str;
$match = "hi(.*)";
$res =~ s/$match/$1/g;

print $res

Das brachte mir die '1234'.

0
rmk