Подтвердить что ты не робот

Как использовать переменную на стороне замены оператора замены Perl?

Я хотел бы сделать следующее:

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

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

Я бы ожидал, что $var будет содержать "foo middle bar", но он не работает. Также нет:

$replace='foo \1 bar';

Как-то я упускаю что-то в связи с побегом.


Я исправил недостающие 's'

4b9b3361

Ответ 1

На стороне замены вы должны использовать $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, см. эффект двойного значения:

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

Ответ 2

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

этот код делает именно то, что вы думаете.

Если ваша строка подстановки находится в веб-приложении, вы просто открыли дверь для выполнения произвольного кода.

Хорошая работа.

Кроме того, он НЕ БУДЕТ работать с включенными taints по этой причине.

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

Тем не менее, более осторожный метод является разумным, безопасным, безопасным и не подрывает потертости. (Будьте уверены, строка, которую она испускает, по-прежнему испорчена, поэтому вы не теряете никакой безопасности.)

Ответ 3

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

Будьте осторожны. Это приводит к возникновению двух слоев eval, по одному для каждого e в конце регулярного выражения:

  • $sub → $1
  • $1 → окончательное значение, в примере 1234

Ответ 4

Как и другие, вы можете использовать следующее:

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;

Я предпочитаю второй для первого, так как он не скрывает того факта, что используется eval(EXPR). Однако обе вышеупомянутые ошибки молчания, поэтому следующее было бы лучше:

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;

Но, как вы можете видеть, все вышеперечисленное позволяет выполнить произвольный код 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);

Ответ 5

Я бы предложил что-то вроде:

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

Это вполне читаемо и кажется безопасным. Если требуется многократная замена, это легко:

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

Ответ 6

См. ЭТО предыдущий пост SO при использовании переменной на стороне замены s/// в Perl. Посмотрите как на принятый ответ, так и на ответ опровержения.

То, что вы пытаетесь сделать, возможно с помощью формы s///ee, которая выполняет двойную eval в правой строке. Более подробно см. perlop quote like operator.

Будьте предупреждены о том, что есть проблемы с безопасностью eval, и это не будет работать в режиме taint.

Ответ 7

#!/usr/bin/perl

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

print $res

Это дало мне "1234".

Ответ 8

Я не уверен в том, чего вы пытаетесь достичь. Но, возможно, вы можете использовать это:

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

т.е. просто оставьте середину в покое и замените начало и конец.