home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


1.8. Expanding Variables in User Input

Problem

You've read in a string with an embedded variable reference, such as:

You owe $debt to me.

Now you want to replace $debt in the string with its value.

Solution

Use a substitution with symbolic references if the variables are all globals:

$text =~ s/\$(\w+)/${$1}/g;

But use a double /ee if they might be lexical ( my ) variables:

$text =~ s/(\$\w+)/$1/gee;

Discussion

The first technique is basically "find what looks like a variable name, and then use symbolic dereferencing to interpolate its contents." If $1 contains the string somevar , then ${$1} will be whatever $somevar contains. This won't work if the use strict 'refs' pragma is in effect because that bans symbolic dereferencing.

Here's an example:

use vars qw($rows $cols);
no strict 'refs';                   # for ${$1}/g below
my $text;

($rows, $cols) = (24, 80);
$text = q(I am $rows high and $cols long);  # like single quotes!
$text =~ s/\$(\w+)/${$1}/g;
print $text;




I am 24 high and 80 long



You may have seen the /e substitution modifier used to evaluate the replacement as code rather than as a string. It's designed for situations such as doubling every whole number in a string:

$text = "I am 17 years old";
$text =~ s/(\d+)/2 * $1/eg; 

When Perl is compiling your program and sees a /e on a substitute, it compiles the code in the replacement block along with the rest of your program, long before the substitution actually happens. When a substitution is made, $1 is replaced with the string that matched. The code to evaluate would then be something like:

2 * 17

If we tried saying:

$text = 'I am $AGE years old';      # note single quotes
$text =~ s/(\$\w+)/$1/eg;           # WRONG

assuming $text held a mention of the variable $AGE , Perl would dutifully replace $1 with $AGE and then evaluate code that looked like:

'$AGE'

which just yields us our original string back again. We need to evaluate the result again to get the value of the variable. To do that, just add another /e :

$text =~ s/(\$\w+)/$1/eeg;          # finds my() variables

Yes, you can have as many /e modifiers as you'd like. Only the first one is compiled and syntax-checked with the rest of your program. This makes it work like the eval {BLOCK} construct, except that it doesn't trap exceptions. Think of it more as a do {BLOCK} instead.

Subsequent /e modifiers are quite different. They're more like the eval " STRING " construct. They don't get compiled until run-time. A small advantage of this scheme is that it doesn't make you put a no strict 'refs' pragma in the block. A tremendous advantage is that unlike the symbolic dereferencing, this mechanism can actually find lexical variables created with my , something symbolic references can never do.

The following example uses the /x modifier to enable whitespace and comments in the pattern part of the substitute and /e to evaluate the right-hand side as code. The /e modifier gives you more control over what happens in case of error or other extenuating circumstances, as we have here:

# expand variables in $text, but put an error message in
# if the variable isn't defined
$text =~ s{
     \$                         # find a literal dollar sign
    (\w+)                       # find a "word" and store it in $1
}{
    no strict 'refs';           # for $$1 below
    if (defined $$1) {
        $$1;                    # expand global variables only
    } else {
        "[NO VARIABLE: \$$1]";  # error msg
    }
}egx;

Note that the syntax of $$1 has changed for Perl 5.004: it used to mean ${$}1 but now means ${$1} . For backwards compatibility, in strings it still takes the old meaning (but generates a warning with -w ). People will write ${$1} within a string to keep from dereferencing the PID variable. If $$ were 23448, then $$1 in a string would turn into 234481 , not the contents of the variable whose name was stored in $1 .

See Also

The s/// operator in perlre (1) and perlop (1) and the "Pattern Matching" and "Regular Expressions" sections of Chapter 2 of Programming Perl ; the eval function in perlfunc (1) and Chapter 3 of Programming Perl ; the similar use of substitutions in Recipe 20.9 .