56

私は次のことをしたいと思います:

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

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

$var には「foo middle bar」が含まれていると思いますが、機能しません。どちらもしません:

$replace = 'foo \1 bar';

どういうわけか、エスケープに関して何かが欠けています。

4

9 に答える 9

88

交換側では、\1ではなく$1を使用する必要があります。

そして、あなたはあなたが望む結果を与える評価可能な式を置き換えて、s ///にそれを次のように/ee修飾子で評価するように指示することによってのみあなたが望むことをすることができます:

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

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

print "var: $var\n";

""とdouble/eが必要な理由を確認するには、ここでdoubleevalの効果を確認してください。

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

(池上が指摘しているように、単一の/eまたは二重のeの最初の/eは実際にはeval();ではなく、置換が文字列ではなくコンパイルするコードであることをコンパイラに通知します。それでも、eval(eval(...))なぜ必要なのかを示しています/ eeを希望どおりに機能させるために必要なことを実行します。)

于 2008-12-25T10:38:46.673 に答える
13

Deparse は、これが実行されていることを示しています。

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

でも、

 /$find/foo \1 bar/

次のように解釈されます。

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

残念ながら、これを行う簡単な方法はないようです。

文字列 eval で実行できますが、危険です。

私のために働く最も正気の解決策はこれでした:

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

ee テクニックに対する反論:

私の答えで言ったように、私は理由で評価を避けます。

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

このコードは、あなたが思っていることを正確に実行します。

置換文字列が Web アプリケーション内にある場合は、任意のコード実行への扉を開いただけです。

よくできた。

また、まさにこの理由から、汚染がオンになっていると機能しません。

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

ただし、より慎重な手法は、正気で、安全で、安全で、汚染に失敗ません。(安心してください、それが発行する文字列はまだ汚染されているので、セキュリティを失うことはありません。)

于 2008-12-25T08:55:30.423 に答える
8

他の人が示唆しているように、次を使用できます。

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;

上記は次の略です。

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

使用されているという事実を隠さないので、私は最初よりも 2 番目を好みeval(EXPR)ます。ただし、上記のサイレンス エラーは両方とも発生するため、次のほうが適切です。

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;

しかし、ご覧のとおり、上記のすべてが任意の Perl コードの実行を許可しています。以下ははるかに安全です。

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);
于 2015-04-15T22:02:37.743 に答える
7
# perl -de 0
$match="hi(.*)"
$sub='$1'
$res="hi1234"
$res =~ s/$match/$sub/gee
p $res
  1234

ただし、注意してください。これにより、正規表現の最後にevalそれぞれ 1 つずつ、2 つのレイヤーが発生します。e

  1. $sub --> $1
  2. $1 --> 最終値、例では 1234
于 2010-07-17T01:05:19.900 に答える
1

Perlでの置換側での変数の使用に関するこの以前のSO投稿を参照してください。受け入れられた答え反論s///の答えの両方を見てください。

あなたがやろうとしていることは、右側の弦でs///eeダブルを実行するフォームで可能です。その他の例については、演算子のようなperlopquoteevalを参照してください。

のセキュリティ上の問題がevalあり、これは汚染モードでは機能しないことに注意してください。

于 2010-07-17T01:24:38.433 に答える
1

私は次のようなものを提案します:

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

かなり読みやすく、安全なようです。複数の交換が必要な場合、それは簡単です:

while ($text =~ m{(.*)$find(.*)}){
     $text = $1 . $replace . $2;
}
于 2010-01-19T16:00:44.113 に答える
1
#!/usr/bin/perl

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

print $res

これで「1234」になりました。

于 2010-07-17T00:01:19.610 に答える
-6

あなたが何を達成しようとしているのかはわかりません。しかし、多分あなたはこれを使うことができます:

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

つまり、真ん中をそのままにして、開始と終了を置き換えます。

于 2008-12-25T10:47:26.703 に答える