Forum: Ruby Interpolating $1, etc., from within a gsub block

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Lloyd Z. (Guest)
on 2009-03-17 21:02
(Received via mailing list)
I'm trying to use the block form of gsub in order to do substitution
involving an interpolation stored within a variable, but I can't
figure out how to get it to work.  Here's a simplified case of what
I'm trying to do:

$fromre = Regexp.compile(ARGV[0])
$repl   = ARGV[1]
$replcount = 0

string = "... something arbitrary ..."

string.gsub!($fromre) {
  |x|
  $replcount += 1
  $repl
}

As you can see, the regexp comes in on the command line, as well as
the replacement. I want to do this substution using the block form so
I can count the number of replacements that were made.

This works as long as $repl doesn't contain any references to matched
patterns. However, if this program is called 'myprog' and I do the
following, the replacement of $1 with the indicated subexpression
doesn't occur:

myprog 'abc(\S+)def' 'NEW-$1-MATCH'

In other words, if the string being substituted is this, "abcFOOBARdef",
the result is not "NEW-FOOBAR-MATCH" as I would like it to be, but
rather,
"NEW-$1-MATCH".

It's clear why this doesn't work, but I can't figure out what construct
to use within the gsub block or on the command line to make sure that it
_does_ work.

I also tried this, and not surprisingly, it didn't work, either:

myprog 'abc(\S+)def' 'NEW-#{$1}-MATCH'

Can anyone suggest how I can accomplish this?

Thanks in advance.
Lloyd Z. (Guest)
on 2009-03-17 21:55
(Received via mailing list)
Lloyd Z. <ljz <at> asfast.com> writes:

> [ ... ]
>
> string.gsub!($fromre) {
>   |x|
>   $replcount += 1
>   $repl
> }
>

Well, I came up with the following, but I'm still wondering if there
is a more elegant way to do this:

string.gsub!($fromre) {
  |x|
  $replcount += 1
  eval "$result = \"#{$to}\""
  $result
}

Then, I have to invoke my program as follows:

myprog 'abc(\S+)def' 'NEW-#{$1}-MATCH'

Is this the best I can do, or is there something more elegant?

Thanks.
matt neuburg (Guest)
on 2009-03-17 22:21
(Received via mailing list)
Lloyd Z. <removed_email_address@domain.invalid> wrote:

>
> This works as long as $repl doesn't contain any references to matched
> patterns. However, if this program is called 'myprog' and I do the
> following, the replacement of $1 with the indicated subexpression
> doesn't occur:
>
> myprog 'abc(\S+)def' 'NEW-$1-MATCH'
>
> In other words, if the string being substituted is this, "abcFOOBARdef",
> the result is not "NEW-FOOBAR-MATCH" as I would like it to be, but rather,
> "NEW-$1-MATCH".
>

$fromre = /abc(\S+)def/
$repl = '"NEW-#{$1}-MATCH"'
string = "abcFOOBARdef"

string.gsub!($fromre) { |x|
  instance_eval($repl)
}

I don't like it any better than you do...! :) m.
Ken B. (Guest)
on 2009-03-18 17:43
(Received via mailing list)
On Tue, 17 Mar 2009 14:52:08 -0500, Lloyd Z. wrote:

>>
> Well, I came up with the following, but I'm still wondering if there is
> a more elegant way to do this:
>
> string.gsub!($fromre) {
>   |x|
>   $replcount += 1
>   eval "$result = \"#{$to}\""
>   $result
> }

I think you're going overboard with variables (and global variables in
particular)

an eval "\"#{$to}\"" would have been sufficient here, and if you really
needed a new variable, then calling it simply result (without the dollar
sign) would have given you a local variable.
Ken B. (Guest)
on 2009-03-18 17:43
(Received via mailing list)
On Tue, 17 Mar 2009 13:59:24 -0500, Lloyd Z. wrote:

>
> This works as long as $repl doesn't contain any references to matched
> It's clear why this doesn't work, but I can't figure out what construct
> to use within the gsub block or on the command line to make sure that it
> _does_ work.
>
> I also tried this, and not surprisingly, it didn't work, either:
>
> myprog 'abc(\S+)def' 'NEW-#{$1}-MATCH'
>
> Can anyone suggest how I can accomplish this?
>
> Thanks in advance.

Variable interpolation only works for string literals inside ruby code.
If you bring in the string from anywhere else, you'll can use eval

eval '"'+string+'"' and hope there are no injection attacks in the
string.

Alternatively, you can roll your own substitution:

string.gsub!($fromre) do |match|
  matchdata=Regexp.last_match
  $repl.gsub(/\$\d+/) do |subst|
     matchdata[subst[1..-1].to_i]
  end
end

--Ken
This topic is locked and can not be replied to.