Как использовать переменную на стороне замены оператора замены 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'
Ответы
Ответ 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/;
т.е. просто оставьте середину в покое и замените начало и конец.