Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi there. I'm new to perl and this problem is *kIlLiNg* me! I'm eager to learn perl though, so any help would be much appreciated! I want to pass the LHS and RHS of an s/// expression in to a subroutine along these lines:
# foo(text, regexp, replacement) # print text with replacements sub foo { my $text; ($text = $_[0]) =~ s/$_[1]/$_[2]/; print $text; } foo("This is test 1\n", "test (.*)", "fish $1");
With the intention of replaceing "test n" with "fish n" using backreferncing. But can I get it to work!? I've tried replacing it with "fish $1", "fish \$1", "fish \\$1" and even "fish \\\$1"! Sadly nothing yields the result I'm after. Could anyone explain why, and how I should do what I'm trying to do?

Replies are listed 'Best First'.
Re: How do I pass $1 to s///?
by ikegami (Patriarch) on Jan 06, 2006 at 16:56 UTC

    Your $1 gets interpolated too early. What you really need is a templating system, but you can use the following in the meanwhile:

    sub foo { (my $text = $_[0]) =~ s/$_[1]/eval $_[2]/e; print $text; } foo("This is test 1\n", qr/test (.*)/, '"fish $1"');

    (Insert notice of hazards of using eval EXPR here.)

      No need to use eval, I'd prefer one of...
      sub foo { (my $text = $_[0]) =~ s/$_[1]/$_[2]->($1)/e; print $text; } foo("This is test 1\n", qr/test (.*)/, sub{"fish $_[0]"});
      ...or...
      sub foo { (my $text = $_[0]) =~ s/$_[1]/$_[2]->()/e; print $text; } foo("This is test 1\n", qr/test (.*)/, sub{"fish $1"});
        Nice, as long as the parameters of foo aren't read a file.
      Slightly nicer as...
      (my $text = $_[0]) =~ s/$_[1]/$_[2]/ee;
        I call that uglier since it hides the eval. eval EXPR is something that should be glaring, blinking and alarming.
      Ah, thankyou! The only problem is that I want to pass a much more complicated string in than "fish $1"... What do you mean by a templating system? Can you point me at a webpage or something? Thanks for the replies BTW! It's most appreciated! (You monks reply fast too! :)

        You can pass any Perl source code in the above snippet (which is why it's potentially dangerous).

        One templating system would be the Template Toolkit.

        use Template; sub foo { my $template = Template->new(); (my $text = $_[0]) =~ s/$_[1]/ my $output = ''; my @matches = map { substr($text, $-[$_], $+[$_] - $-[$_]) } 1 .. $#-; $template->process(\$_[2], { matches => \@matches }, \$output); $output /e; print $text; } foo("This is test 1\n", qr/test (.*)/, 'fish [% matches.1 %]');

        (Untested)

Re: How do I pass $1 to s///?
by Happy-the-monk (Canon) on Jan 06, 2006 at 16:54 UTC

    backreferences in perlre are used to be written \1, \2 ... tweetiepooh informs me that my perldoc used was outdated... and ikegami again corrects that.

    Update: your programme prints "This is fish".
    What did you expect it to print? "This is fish 1"?

    Further update: see ikegami's solution below.

    Third and then fourth update - in italics

    Cheers, Sören

      Still not quite right.

      Backreferences are \1, \2, etc in the LHS (regexp part) of the substitution.
      Backreferences are $1, $2, etc in the RHS (substitituion part) of the substitution.

      Lastest perlre (via [doc://perlre])

      \1,\2

      If warnings are on using these lead to
      \1 better written as $1

      There is a section in perlre about it.
      > your programme prints "This is fish".
      ...and gives a warning about $1 being uninitialised :(

      > What did you expect it to print? "This is fish 1"?
      Yeah. I've tried "fish \1", "fish \\1", "fish \\\1" as well though with no more luck!