Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister

Double Interpolation of a String

by chromatic (Archbishop)
on May 18, 2000 at 04:45 UTC ( #12275=snippet: print w/replies, xml ) Need Help??
Description: Suppose you have a string with variable names in it. You want them to be expanded when you print it, but you're not sure how. Never fear, you can abuse eval()!
my $color = "red";
my $fruit = "apple";
my $name = "chromatic";

my $string = 'Hi, my name is $name.  Please hand me a $color $fruit.';
print ">>$string<<\n";    # demonstrate what we have

my $s2;
eval "\$s2 = qq/$string/";    # the real magic
print "->$s2<-\n";    # demonstrate the result
Replies are listed 'Best First'.
RE: Double Interpolation of a String
by davorg (Chancellor) on Jun 22, 2000 at 14:30 UTC

    For doing stuff with these kinds of substitutions, I always use code like this:

    use strict; my %trans = (color => 'red', fruit => 'apple', name => 'chromatic'); my $string = 'Hi, my name is $name. Please hand me a $color $fruit.'; $string =~ s/\$(\w+)/$trans{$1}/eg; print $string;


    European Perl Conference - Sept 22/24 2000
      With inspiration from davorg's above example, taking it a step further (and a step further (and a step further (...))):
      #!/usr/bin/perl -w use strict; my %trans = ( '$name' => 'Frank', '$color' => 'Cherry Red', '$time' => sprintf ("%d:%02d:%02d", (localtime(time))[2,1,0]), '$date' => sprintf ("%04d-%02d-%02d", (localtime(time))[5] + 1900, (localtime(time))[4] + 1, (localtime(time))[3]), '$dstr' => '$time on $date', ); my ($trans_rx) = join ('|', sort { length($b) - length($a) } sort map { quotemeta ($_) } keys %trans); my ($string) = 'I\'m $name and I prefer $color objects, at least +on $dstr'; while ($string =~ s!($trans_rx)!$trans{$1}!o) { } print "$string\n";
      For output like:
      I'm Frank and I prefer Cherry Red objects, at least on 15:33:43 o +n 2001-02-06
      This version doesn't replace stray references to things like $1.95 with nothing, but it might get caught in a loop if you specify two entries which convert to eachother recursively such that "$a -> $b" and "$b -> $a".
        It might be useful to protect against infinite recursion as well:
        my $max_recurs = 10; $max_recurs-- while $string =~ ... && $max_recurs; warn "Too many recursive substitutions on '$string'" if !$max_recurs && $^W;
Re: Double Interpolation of a String
by nontrivial (Novice) on Aug 21, 2001 at 06:20 UTC
    Brother chromatic, I am not having much success reproducing this. If I create this simple script it works. However, if I try it in an application I am working on it doesn't. After a few hours of frustration I have decided to punt. Help please? Here is a chunk of my code. I am pulling the string out of the database instead of arbitrarily setting it:
    my $WhereVal = $Return->[0]->{WhereVal}; my $Temp2 = 2; warn $WhereVal; eval "\$Temp = qq/$WhereVal/"; warn $Temp;
    The result is that the eval fails. The log shows:
    c.AppModule=m.AppModule and m.Application=$Temp2 at (eval 43) line 19. Use of uninitialized value in concatenation (.) at (eval 43) line 20. Use of uninitialized value in warn at (eval 43) line 21.
    The complete subroutine, less gratuitous debugging code:
    sub VWP_RunQuery { my ($QueryRef, @Params) = @_; if ($QueryRef) { VWP_Log(5, "Running query $QueryRef..."); my $stt = "select q.FromVal, q.OrderVal, q.SelectVal, q.WhereVal " + . "from VWPQuery q, VWPQueryRef qr where q.Query=qr.Query and " . "qr.DBType = ? and qr.Query = ?"; my $Return = VWP_SQL($stt, ($Session::DBType, $QueryRef)); if (@$Return) { my $OrderVal = $Return->[0]->{OrderVal}; $stt = "select " . $Return->[0]->{SelectVal} . " from " . $Return->[0]->{FromVal}; if ($Return->[0]->{WhereVal}) { my $WhereVal; eval "\$WhereVal = qq/$Return->[0]->{WhereVal}/"; $stt = $stt . " where " . $WhereVal; } if ($OrderVal) { if (($OrderVal =~ /order by/) || ($OrderVal =~ /ORDER BY/)) { $stt = $stt . " " . $OrderVal; } else { $stt = $stt . " order by " . $OrderVal; } } return VWP_SQL($stt, @Params); } } VWP_StrMessage(1, 13, $QueryRef); }
      It's possible that $stt is empty in some of the concatenations. A couple of minor style issues might clear it up.

      Otherwise, it depends a lot on what's coming back from the database. Without seeing that, it's hard to know what the trouble is. As a side note, I personally wouldn't use this for such a thing. It's very hard to control what variables are in scope and accessible when you pull information out of a database like this.

      There's probably a better way. If you describe the situation in SoPW, you'll probably get two or three alternate ideas that aren't as fragile.

      sub VWP_RunQuery { my ($QueryRef, @Params) = @_; if ($QueryRef) { VWP_Log(5, "Running query $QueryRef..."); my $stt = "select q.FromVal, q.OrderVal, q.SelectVal, q.WhereV +al " . "from VWPQuery q, VWPQueryRef qr where q.Query=qr.Query and " +. "qr.DBType = ? and qr.Query = ?"; my $Return = VWP_SQL($stt, ($Session::DBType, $QueryRef)); if (@$Return) { my $OrderVal = $Return->[0]->{OrderVal}; $stt = "select " . $Return->[0]->{SelectVal} . " from " . $Return->[0]->{FromVal}; if ($Return->[0]->{WhereVal}) { my $WhereVal = eval "qq/$Return->[0]->{WhereVal}/"; $stt .= " where " . $WhereVal; } if ($OrderVal) { $stt .= " order by" unless $OrderVal =~ /^order by/i; $stt .= " $OrderVal"; } return VWP_SQL($stt, @Params); } } VWP_StrMessage(1, 13, $QueryRef); }
Re: Double Interpolation of a String
by 5mi11er (Deacon) on Feb 01, 2007 at 22:26 UTC
    Having just needed something along these lines, and after reading through the Dreaming of Post Interpolation thread, I think I've settled on using something like this:
    @lines = <<TEXT_INFO; Dear \$person, I know that this text is \$adjective. But I wish it could be... This is a test of imbedded variables I want to set \\\$variable later, and then have it interepreted when + printed variable was set to \$variable TEXT_INFO $variable = 5; my $person = 'Mom'; my $adjective = 'not interpolated'; foreach $line (@lines) { print eval "qq{$line}"; }
Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: snippet [id://12275]
[Corion]: hippo: If you have a Unicode-wise Perl then likely some zero-width characters in $x would work. Maybe $x = "\x{200b}" works.
[Corion]: Hmm - no, that outputs 1 for me on 5.14 - perl -wle "my $x = qq(\x{200b}); warn $x; warn length $x"
[hippo]: Smart - I'll give that a go. Thanks.
[hippo]: Ah
[Corion]: But maybe there is some other Unicode string that will be true but have a zero width
[hippo]: For explanation, I've seen this construct in someone else's code (no names, no pack drill) and couldn't think of a situation to trigger it.
[Corion]: You'll have to look somewhere esoteric for that. Maybe some tied variable or special dualvar can also trigger that. But it's certainly not a common occurrence
[Corion]: And on 5.20, the following also outputs no find:perl -wle 'for my $x ("\x{2000}".."\ x{1fffff}") { if( $x && ! length $x ) { warn qq(<$x>); warn length $x; die } }'
[Corion]: (this time on Unix)

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (9)
As of 2017-07-27 13:28 GMT
Find Nodes?
    Voting Booth?
    I came, I saw, I ...

    Results (413 votes). Check out past polls.