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

Hello,

I have some code that I'm working on that needs to be able to set system variables similar to 'source /my/config.cfg' in unix. I've found a really neat way to convert these to perl variables dynamically with the following code:

#!/usr/bin/perl # Set up the environment variables and dynamic variables from /my/conf +ig.cfg foreach (`. /my/config.cfg; set`) { chomp; my ($var,$val) = /^\s*(.*?)\s*=\s*(.*)/; ${$var} = $val; }

if my config file had the following:

myvar1 = foo myvar2 = bar

I should get the equivalent perl variables '$myvar1="foo"; $myvar2 = "bar";'...

This works well until I add 'use strict;' which is what you'd expect...I have to add 'no strict "vars"; no strict "refs";' in order to have the code work...

I've looked on the web and around the monks site but I haven't hit on a solution that will allow me to use strict without crippling it. I've read several perl faqs for pulling embedded vars out of text files using eval but nothing I've come across covers this situation exactly.

Looking for a pearl of wisdom that will make this all better (I am currently crippling strict).

-sol

Update...thanks for all the input...a hash is probably best for my needs...I'm thinking of using the $ENV{$var} hash to make them environment variables...probably should have done that to start with but it was 3 AM and I wasn't thinking too clearly.

Replies are listed 'Best First'.
Re: Dynamic variables and strict
by dewey (Pilgrim) on May 02, 2007 at 20:44 UTC
    My instinct here would be to make a hash.
    #untested code! my %conf = (); foreach (`. /my/config.cfg; set`) { chomp; my ($var,$val) = /^\s*(.*?)\s*=\s*(.*)/; $conf{$var} = $val; }
    This would tell you (and anyone who reads your code!) that values accessed using the hash came from outside the program. It also seems to clean up uninitialized varible issues... I don't know, it just seems right to use a hash to relate keys to values.

    ~dewey
      As dewey said, hashes are the way to go. As long as you remember to check that s/// matched.

      Instead of

      my ($var,$val) = /^\s*(.*?)\s*=\s*(.*)/; $conf{$var} = $val;
      I'd use
      if (/^\s*(.*?)\s*=\s*(.*)/) { $conf{$1} = $2; } else { die "slowly and painfully"; }
Re: Dynamic variables and strict
by kyle (Abbot) on May 02, 2007 at 20:44 UTC

    I don't know the rest of your code, but my strong recommendation would be to put the config variables in a hash. Instead of "${$var} = $val", you'd say "$config_value_for{$var} = $val" and then access those values through the hash. Better still, use something like Config::Std—if your situation allows for that. If you really have to have Perl variables of the same name as what's in the config, what you're doing is what you have to do, but I think you'd probably be doing yourself a favor by retooling the rest of your code to remove that requirement.

Re: Dynamic variables and strict
by varian (Chaplain) on May 03, 2007 at 06:24 UTC
    I would much agree on the remarks made by other monks, to use a hash for your config vars is a much cleaner and safer approach.

    Having said that, your idea of using an eval can work as per below example. Just keep in mind that:

    • eval runs as a nested block, therefore any variables declared within eval are lost once eval is done. Hence you you need to declare your variables before using them in the eval.
    • make sure that your eval cannot be abused thru evil data in a configfile. So check all input before you use it in eval, make sure to single-quote string so that they do not get interpreted, etc etc.
    #!/usr/bin/perl use warnings; use strict; my @accepted_vars = qw( myvar1 myvar2 ); my $myvar1; # must declare variable beforehand! my $myvar2; my @lines = <DATA>; chomp @lines; foreach (@lines) { if ( /^\s*(.*?)\s*=\s*(.*)/ && grep $1 eq $_ , @accepted_vars ) { eval '$'."$1 = '$2'"; warn "eval failed: $@\n" if $@; } else { warn "Illegal config var '$1'\n"; } } print "myvar1 = '$myvar1'\n", "myvar2 = '$myvar2'\n"; __DATA__ myvar1 = foo myvar2 = bar evil_var = foo
    Output from program is:
    Illegal config var 'evil_var' myvar1 = 'foo' myvar2 = 'bar'
Re: Dynamic variables and strict
by McDarren (Abbot) on May 03, 2007 at 04:59 UTC
Re: Dynamic variables and strict
by naikonta (Curate) on May 03, 2007 at 15:40 UTC
    Just because you can do something with other tools, doesn't mean that you do it in Perl in the exact way. In fact, you can achieve the same result in a absolutely different way as shown by many monks above. While in some (very rare, IMO) circumtances it may be convenient to do what you intended, and it's truly prove of TMTOWTDI, but when we're talking about Real Code, turning arbitrary strings into distinct variables is just not the way. I personally think of it as an evil trap, would give much headache for me. They will clutter your code, after all.

    Again, yes, you can do that in Perl. But, you can do it not because Perl encourages to do so. You can do it because the powerful way to do it (eval) is designed to do much more useful and flexible stuff, yet leads to disaster if used improperly. Don't you just realize that "eval" is only a byte away from "evil"? ;-)

    As to solution for your Real Problem (assuming it's really about configuration parsing), I would prefer to search on CPAN for various Config packages.


    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!