'Eval'uating a variable

P

Prabh

Hello all,
I've a question about the using the eval function at runtime.
I can evaluate a varible, $foo, as

my $bar = eval($foo) ;

But when theres some extra string at the end of $foo, the eval fails
to return any value,

my $bar = eval($foo/moretext) ;

The value of $foo is to be interpolated at the run-time.
If at runtime it interpolates as, "/home/123user", is it possible for
me to eval my $bar as "/home/123user/moretext".

Reason I ask,
I've a file full of property settings, one of which sets the location
of logfile, as follows,

logfile = ${USER_HOME}/logfiles/log.txt

My program traverses through this properties file, finds a match for
logfile setting, gets the value of this setting and needs to expand
the USER_HOME variable as follows,

==============================================================================
if ( $line =~ /^logfile/ )
{
my $log_to_file = (split(/(\s)*\=(\s)*/, $line))[1] ;

# BTW, is 'split' the best way to obtain the value.
# I tried using the regular expressions, but cant find a way to do
# w/o an 'if conditional.'
}

$log_to_file_after_eval = eval($log_to_file) ;

==============================================================================

The eval returns null. So, I'm doing it the hacky way, extracting the
environment variable part of it ( the text between "${" and "}" ) and
splitting it from the rest of the setting value.

==============================================================================
if ( $log_to_file =~ m/^\s*\$\{(.*)\}(.)*/ )
{
$user_home_path = $1 ;
$rest_of_path = $2 ;
# Put the "${ENV" in front of it, to supply it to eval.
$user_home_path = '$ENV{'."$user_home_path"."}' ;
}

$after_eval = eval($user_home_path} ;
$user_home_after_eval = "$after_eval"."$rest_of_path" ;
print "This is path after eval: $usr_home_after_eval\n";

==============================================================================

I'm wondering if theres a more elegant solution for this.
I've a feeling this might get more and more complicated, like some
users may not wrap the variable with {}.

Thanks for your time,
Prab
 
G

Gunnar Hjalmarsson

Prabh said:
But when theres some extra string at the end of $foo, the eval
fails to return any value,

my $bar = eval($foo/moretext) ;

The value of $foo is to be interpolated at the run-time. If at
runtime it interpolates as, "/home/123user", is it possible for me
to eval my $bar as "/home/123user/moretext".

my $bar = eval($foo) . '/moretext';

The rest of your questions make little sense without sample data.
 
A

Ala Qumsieh

Prabh said:
logfile = ${USER_HOME}/logfiles/log.txt

My program traverses through this properties file, finds a match for
logfile setting, gets the value of this setting and needs to expand
the USER_HOME variable as follows,

The special %ENV hash will contain all your environment variables. Check it
out in perlvar.
============================================================================
==
if ( $line =~ /^logfile/ )
{
my $log_to_file = (split(/(\s)*\=(\s)*/, $line))[1] ;

# BTW, is 'split' the best way to obtain the value.
# I tried using the regular expressions, but cant find a way to do
# w/o an 'if conditional.'
}

$log_to_file_after_eval = eval($log_to_file) ;

This code is broken since you declar $log_to_file as a lexical variable
within the if() block, but attempt to use it outside the block. Also, you
seem to miss the point of eval(). It treats its first argument as either a
Perl expression or a block of Perl code and try to run it and return its
return value. In your case, it will treat whatever is inside $log_to_file
(which should be '${USER_HOME}') as a Perl snippet, which will return undef
since $USER_HOME is not defined (from what we see).

As for your code, I'd re-write it this way (untested):

my $log_to_file;
if ($line =~ /^logfile\s+=\s+(\S+)/) {
$log_to_file = $1;
$log_to_file =~ s/\{(\S+?)\}/$ENV{$1}/g;
}

This is ofcourse a rather simplistic approach, but you don't give us too
much data to work with :)
============================================================================
==

The eval returns null. So, I'm doing it the hacky way, extracting the
environment variable part of it ( the text between "${" and "}" ) and
splitting it from the rest of the setting value.

============================================================================
==
if ( $log_to_file =~ m/^\s*\$\{(.*)\}(.)*/ )
{
$user_home_path = $1 ;
$rest_of_path = $2 ;
# Put the "${ENV" in front of it, to supply it to eval.
$user_home_path = '$ENV{'."$user_home_path"."}' ;
}

$after_eval = eval($user_home_path} ;
$user_home_after_eval = "$after_eval"."$rest_of_path" ;
print "This is path after eval: $usr_home_after_eval\n";

Why not simply:
$after_eval = $ENV{$user_home_path};

??

--Ala
 
D

Darin McBride

Prabh said:
Hello all,
I've a question about the using the eval function at runtime.
I can evaluate a varible, $foo, as

my $bar = eval($foo) ;

But when theres some extra string at the end of $foo, the eval fails
to return any value,

my $bar = eval($foo/moretext) ;

From what I can tell, this is:

* Take $foo.
* Divide it by the return from moretext().
* evaluate the number so returned.

Perhaps if you used strict and warnings, something useful would have
popped up....?
The value of $foo is to be interpolated at the run-time.
If at runtime it interpolates as, "/home/123user", is it possible for
me to eval my $bar as "/home/123user/moretext".

As posted by Gunnar: C<EXPR1 . EXPR2> is what you're looking for.
Specifically, eval($foo) . "/moretext". Personally, I'd do:
File::Spec->catfile(eval($foo), 'moretext');
But that's because I like my stuff to look cross-platform, even if the
concept is uni-platform. For example, one script I have wraps around
rpm - that's definitely Linux-only (well, and AIX, but that's still
unix). But I still use File::Spec to show that I'm working with file
specifications (full pathnames), not just arbitrary strings.
Reason I ask,
I've a file full of property settings, one of which sets the location
of logfile, as follows,

logfile = ${USER_HOME}/logfiles/log.txt

There are sooooo many ways to do this that I'll forget some.

1. Heavy-handed, slow, and deadly accurate:

Filter the file with s/\s*=\s*/=/ into another file. Then use
C<open(CFG,". $tmpfile; set|")> (with appropriate error handling). Each
line from this will be already evaluated properly. You'll end up with
too much stuff, though, things like PATH, LIB_PATH, etc, but the config
parms you want will be in there, too. But you don't need to worry
about things like differentiating between $USER_HOME and ${USER_HOME}
and ${USER_HOME?} and ${USER_HOME#*/} and ...

Of course, this is extremely non-portable. Perhaps that's not a
concern for you in this case.

2. Portable, perhaps fast (no subprocesses):

Use the following re (or something similar - this is untested) to read
it in:

open(CFG, "...") or die "can't open cfg file";
while (<CFG>)
{
s/\$(?:{(\w+)}|(\w+))/$ENV{$1||$2}/eg;
if (/^\s*(\w+)\s*=\s*(\w+)/)
{
$cfg{lc $1} = $2;
}
}
close CFG;

# now use $cfg{logfile} as your log_to_file variable.


Another small variation may be to allow the cfg file to refer to
earlier variables as if they override the environment:

s/\$(?:{(\w+)}|(\w+))/$cfg{$1||$2}||$ENV{$1||$2}/eg;

Now you can set USER_HOME at the top of the config file, and all
further occurances of $USER_HOME will use that one.

All without any use of eval. :)
The eval returns null. So, I'm doing it the hacky way, extracting the
environment variable part of it ( the text between "${" and "}" ) and
splitting it from the rest of the setting value.

==============================================================================
if ( $log_to_file =~ m/^\s*\$\{(.*)\}(.)*/ )
{
$user_home_path = $1 ;
$rest_of_path = $2 ;
# Put the "${ENV" in front of it, to supply it to eval.
$user_home_path = '$ENV{'."$user_home_path"."}' ;

Tricky stuff. Why bother with eval? Just use

$user_home_path = $ENV{$user_home_path} ;

Note that you're assuming that ${...} appears at the front of your
string, and only once. What if the user does:

logfile = ${USER_HOME}/${LOGDIR}/logfile.txt

? How much of this stuff do you really want to support? :)
}
$after_eval = eval($user_home_path} ;
$user_home_after_eval = "$after_eval"."$rest_of_path" ;
print "This is path after eval: $usr_home_after_eval\n";

==============================================================================

I'm wondering if theres a more elegant solution for this.
I've a feeling this might get more and more complicated, like some
users may not wrap the variable with {}.

You can define your own config file language. If you want to force
users to use {}, do so. :)

if (/\$[^{]/ or /\$[^}]*$/)
{
die "$ without {}!"
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top