'Eval'uating a variable

Discussion in 'Perl Misc' started by Prabh, Feb 2, 2004.

  1. Prabh

    Prabh Guest

    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
     
    Prabh, Feb 2, 2004
    #1
    1. Advertising

  2. Prabh wrote:
    > 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.

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
     
    Gunnar Hjalmarsson, Feb 2, 2004
    #2
    1. Advertising

  3. Prabh

    Ala Qumsieh Guest

    "Prabh" <> wrote in message
    news:...
    >
    > 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
     
    Ala Qumsieh, Feb 2, 2004
    #3
  4. Prabh wrote:

    > 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 {}!"
    }
     
    Darin McBride, Feb 3, 2004
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric Newton
    Replies:
    3
    Views:
    9,605
    Brock Allen
    Apr 4, 2005
  2. DataBinder.Eval and Eval.

    , Jun 16, 2006, in forum: ASP .Net
    Replies:
    1
    Views:
    581
    Karl Seguin [MVP]
    Jun 16, 2006
  3. Alex van der Spek

    eval('07') works, eval('08') fails, why?

    Alex van der Spek, Jan 8, 2009, in forum: Python
    Replies:
    6
    Views:
    1,547
    Bruno Desthuilliers
    Jan 8, 2009
  4. Liang Wang
    Replies:
    8
    Views:
    156
    Ben Morrow
    Feb 2, 2008
  5. Marc Girod

    to eval or not to eval?

    Marc Girod, Apr 19, 2011, in forum: Perl Misc
    Replies:
    2
    Views:
    175
    Marc Girod
    Apr 19, 2011
Loading...

Share This Page