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

Hi, I am having a problem with a search replace.
#!/usr/bin/perl -w my %userInfo; $userInfo{fname} = '__fname__'; $userInfo{lname} = '__lname__'; $userInfo{email} = '__fullFrom__ <__email__>'; my $fname = 'John'; my $lname = 'Doe'; my $fullFrom = join(' ', $fname, $lname); my $email = 'john@doe.com'; foreach my $key (keys %userInfo) { $userInfo{$key} =~ s/__(.+?)__/\$$1/g; print $userInfo{$key}, "\n"; }
The ouput is:

$fname
$fullFrom <$email>
$lname
I want the output to be:

John
John Doe <john@doe.com>
Doe
Am I doing something wrong? If i do the code below:
#!/usr/bin/perl -w my %userInfo; $userInfo{fname} = '__fname__'; $userInfo{lname} = '__lname__'; $userInfo{email} = '__fullFrom__ <__email__>'; my $fname = 'John'; my $lname = 'Doe'; my $fullFrom = join(' ', $fname, $lname); my $email = 'john@doe.com'; foreach my $key (keys %userInfo) { $userInfo{$key} =~ s/__(.+?)__/$$1/g; print $userInfo{$key}, "\n"; }
I get the following output: Use of uninitialized value in substitution iterator at ./foo2.pl line 15. Use of uninitialized value in substitution iterator at ./foo2.pl line 15. Use of uninitialized value in substitution iterator at ./foo2.pl line 15. <> Use of uninitialized value in substitution iterator at ./foo2.pl line 15. Any thoughts on this? Thanks

Replies are listed 'Best First'.
Re: search replace (interpolation)
by tachyon (Chancellor) on Dec 16, 2002 at 20:00 UTC

    First using symbolic references in this way is a hallmark of bad program design. There really is not any good reason to do it the way you are trying to. See this excelent article Why it's stupid to use a variable as a variable name

    Anyway it will work if you remove the my declarations as shown:

    #!/usr/bin/perl -w my %userInfo; $userInfo{fname} = '__fname__'; $userInfo{lname} = '__lname__'; $userInfo{email} = '__fullFrom__ <__email__>'; $fname = 'John'; $lname = 'Doe'; $fullFrom = join(' ', $fname, $lname); $email = 'john@doe.com'; foreach my $key (keys %userInfo) { $userInfo{$key} =~ s/__([^_]+)__/${$1}/g; print $userInfo{$key}, "\n"; } C:\>perl test.pl Name "main::fullFrom" used only once: possible typo at test.pl line 11 +. Name "main::email" used only once: possible typo at test.pl line 12. John John Doe <john@doe.com> Doe C:\>

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: search replace (interpolation)
by Aristotle (Chancellor) on Dec 16, 2002 at 20:04 UTC

    What you're doing wrong is using symbolic references - read the excellent archived newsposts by Dominus, part one, two and three on Why it's stupid to 'use a variable as a variable name'.

    You probably want to use a hash, or maybe hard references, instead (and use strict as well, of course). If you tell us what exactly you're trying to do, we might be able to propose something better suited.

    Makeshifts last the longest.

      What I am trying to do is replace the envelope headers of a email address dynamically.

      The psuedo is as follows:

      Read email template with tokens in the envelope headers:
      Message-ID: <1039751337.22128@idhost> Content-Transfer-Encoding: 8bit Content-Type: multipart/alternative; boundary="_----------=_1039751337 +22128" MIME-Version: 1.0 Date: Thu, 12 Dec 2002 19:48:57 PST From: __FULLFROM__ <__FROM__> To: __FNAME__ __LNAME__ <__EMAIL__> Subject: __SUBJECT__ Precedence: bulk

      I now break up the email template into two parts. 1 is a hash containing all the header information the other is a variable with the body.

      I then want to hit a db and dynamically replace the headers with the fields in the db and send out the email.

        So put the information you retrieve from the database in a hash with the keys FULLFROM, FROM, FNAME, LNAME, EMAIL, and SUBJECT.
        my %header_line = read_header_or_something($mail_file); while(my %recipient_info = get_info_from_db()) { while(my ($header, $value) = each %header_line) { $value =~ s/__(.+?)__/$recipient_info{$1}/g; print "$header: $value\n"; } }

        And I hope you're not sending the mail to people who haven't opted in on their own.. :)

        Update: doh, silly me for forgetting to mention you would be better off with a templating system. Unlike UnderMine, I would propose the more generic and powerful Template Toolkit though.

        Makeshifts last the longest.

        This would be a hell of a lot more robust, maintainable and less obfuscated.

        my $email_template = get_email_template_as_string(); my $sql = 'SELECT first_name, last_name, email FROM address_book'; my $sth = $dbh->prepare_cached{$sql} or die 'Error'; while ( my ($first_name, $last_name, $email) = $sth->fetchrow_array() +) { my $text = $email_template; $text =~ s/__FNAME__/$first_name/; $text =~ s/__LNAME__/$last_name/; # contiue with substitutions send_email($text); }

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: search replace (interpolation)
by gjb (Vicar) on Dec 16, 2002 at 22:31 UTC

    You're essentially filling out a template, so why not using one of the many template modules available at CPAN? Personally I'd use Text::Template for a job like this.

    Just my 2 cents, -gjb-

Re: search replace (interpolation)
by Ionizor (Pilgrim) on Dec 16, 2002 at 20:09 UTC

    It looks like what you're trying to do is use a variable name as a variable name - that's a security no-no.

    If you want the values in the hash, why not put them there directly, such as:

    $userInfo{fname} = 'John'; $userInfo{lname} = 'Doe'; $userInfo{email} = "$userInfo{fname} $userInfo{lname} <john@doe.com>";

    Without knowing more about why you're putting these values into the hash it's hard to give you any better advice.

    Update: Fixed the code so it'll actually work -- $userInfo{fname} != $fname