Mihran Hovsepyan
Mihran Hovsepyan

Reputation: 11088

How explicitly resolve variables in a perl string?

In my perl script I want to have both versions of $config directory:

my $config='$home/client/config';

and

my $config_resolved="$home/client/config";

But I want to get $config_resolved from $config, i.e. something like this:

my $config_resolved=resolve_vars($config);

How can I do such thing in perl?

Upvotes: 3

Views: 3432

Answers (4)

Dave Cross
Dave Cross

Reputation: 69224

From the Perl FAQ (which every Perl programmer should read at least once):

How can I expand variables in text strings?

(contributed by brian d foy)

If you can avoid it, don't, or if you can use a templating system, such as Text::Template or Template Toolkit, do that instead. You might even be able to get the job done with sprintf or printf:

my $string = sprintf 'Say hello to %s and %s', $foo, $bar;

However, for the one-off simple case where I don't want to pull out a full templating system, I'll use a string that has two Perl scalar variables in it. In this example, I want to expand $foo and $bar to their variable's values:

my $foo = 'Fred';
my $bar = 'Barney';
$string = 'Say hello to $foo and $bar';

One way I can do this involves the substitution operator and a double /e flag. The first /e evaluates $1 on the replacement side and turns it into $foo. The second /e starts with $foo and replaces it with its value. $foo, then, turns into 'Fred', and that's finally what's left in the string:

$string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'

The /e will also silently ignore violations of strict, replacing undefined variable names with the empty string. Since I'm using the /e flag (twice even!), I have all of the same security problems I have with eval in its string form. If there's something odd in $foo, perhaps something like @{[ system "rm -rf /" ]}, then I could get myself in trouble.

To get around the security problem, I could also pull the values from a hash instead of evaluating variable names. Using a single /e, I can check the hash to ensure the value exists, and if it doesn't, I can replace the missing value with a marker, in this case ??? to signal that I missed something:

my $string = 'This has $foo and $bar';
my %Replacements = (
    foo  => 'Fred',
    );
# $string =~ s/\$(\w+)/$Replacements{$1}/g;

$string =~ s/\$(\w+)/
            exists $Replacements{$1} ? $Replacements{$1} : '???'
            /eg;
print $string;

Upvotes: 5

Borodin
Borodin

Reputation: 126722

This is most tidily and safely done by the double-eval modifier on s///.

In the program below, the first /e evaluates the string $1 to get $home, while the second evaluates $home to get the variable's value HOME.

use strict;

my $home = 'HOME';

my $config = '$home/client/config';

my $config_resolved = resolve_vars($config);

print $config_resolved, "\n";

sub resolve_vars {
  (my $str = shift) =~ s/(\$\w+)/$1/eeg;
  return $str;
}

output

HOME/client/config

Upvotes: -1

NedStarkOfWinterfell
NedStarkOfWinterfell

Reputation: 5153

Because you are using my to declare it as private variable, you might as well use a /ee modifier. This can find variables declared to be in local scope:

$boo =~ s/(\$\w+)/$1/eeg;

Upvotes: 1

gaussblurinc
gaussblurinc

Reputation: 3682

I use eval for this. So, you must replace all scalars (their names) with their values.

$config = 'stringone';
$boo = '$config/any/string';
$boo =~ s/(\$\w+)/eval($1)/eg;
print $boo;

Upvotes: 2

Related Questions