http://qs1969.pair.com?node_id=344096

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

Hi Monks,

My code is like this:

$sop = xx #$sop read from user input #$sop is <,>,>=,<=,==,!= $sport = xx #$sport read from user input #$sport is tcp or udp port $code = "if($port_a $op $port_b ){"; $code .= "$result =1;}"; $code .= "else{$result=0;}"; while(<FH>) { @tmpdata = split /:/; $port_a = $tmpdata[8]; $op = $sop; $port_b = $sport; eval $code; if ($result) { #do something } else { #do something } } But perl don't support that . So I must use like that: while(<FH>) { @tmpdata = split /:/; if ( ($sop eq "<") and ($tmpdata[8]<$sport ) ) { #do something } elsif( ($sop eq ">") and ($tmpdata[8]>$sport ) { #do something } #and other op like <=,>=,==,!= }

So are there any more efficient way to do that ?

my last question related How to use eval?

thanks!

Edited by BazB: fixed link to use Perlmonks tags

Replies are listed 'Best First'.
Re: How to do that with eval ?
by tachyon (Chancellor) on Apr 10, 2004 at 07:59 UTC

    So are there any more efficient way to do that ?

    A better question might be is using eval the most inefficient , least secure and most incomprehensible method? To which the answer is yes.

    Now seriously string eval and user input or even file input is a recipe for disaster. As you currently don't seem to understand why or how all I can do is suggest you avoid eval "this" until you understand how dangerous it really is.

    If you were to outline what you are actually trying to do (as opposed to how do I take this loaded gun, put it to my head and wait for someone to pull the trigger ;-) I am sure you will find lot's of efficient, secure and fast suggestions.

    From the information you give the if/elsif/elsif/else structure you show IS the most efficient structure in terms of speed and security. There are many other ways to do it, typically a dispatch hash, but these are all slower. Just about anything will be more secure than a string eval where you let user input in.

    my %dispatch = ( '>' => sub { $_[0] > $_[1] }, '<' => sub { $_[0] < $_[1] }, '=='=> sub { $_[0] == $_[1] }, ); # now in loop if ( $dispatch{$sop} ) { # OK so $sop exists, get the right function from # the dispatch hash and call it with the args we want if ( &{$dispatch{$sop}}( $tmpdata[8], $sport ) ) { # yabada } else { # ! yabada } } else { die "Illegal op $sop. Are you trying to hack me?\n"; } # end loop

    cheers

    tachyon

      Just to be clear, an if/elsif/elsif/.../else structure is typically faster than a dispatch hash, but doesn't scale as well to large numbers of possibilities. Scalability is like an 18-wheeler, great for doing a lot with lots of data, but for daily use the sports car is likely faster and loads more fun.

      A better reason to use a dispatch hash is because you prefer how it lets you organize a piece of code.

      my %dispatch = map { $_ => eval sprintf 'sub { $_[0] %s $_[1] }', $_ } qw/ < <= == >= > /;

      Just to abstract it one more level (and get to use eval() constructively again). :-)

      (There's a possibility that I'd do this in real code since I've become such an OAOO-junkie. Am I being a bit extreme here? (I usually needn't bother about speed efficiency.))

      ihb

Re: How to do that with eval ?
by davido (Cardinal) on Apr 10, 2004 at 07:40 UTC
    One problem is that you're ignoring double-quoteish variable interpolation.

    Consider the following:

    my $value = 100; my $string = "$value"; print $string, "\n"; __OUTPUT__ 100

    Ok, here's the problem. What you just witnessed is $value being interpolated as a variable into a string, and assigned to $string. Seems harmless. But it can easily get you into trouble.

    my $var = "Hello world!"; my $evalcode = "print $var, qq/\n/;"; eval $evalcode or print "There was a problem:\n$@\n"; __OUTPUT__ There was a problem: syntax error at (eval 1) line 1, near "!,"

    The problem was that before eval got a chance to see $var, it got interpolated by the double quotes and thus was seen by eval as a literal string rather than a variable. The code that eval saw was:
    print Hello world, qq/\n/;

    See the error? You wanted eval to see
    print $var, qq/\n/;. Instead, you got an unquoted literal string that looked a lot like a bunch of garbage to Perl.

    The situation can be remedied by either using single quotes, or by escaping your double-quoted variables so that eval gets them uninterpolated. It can be tricky deciding which option to use. ...but that's the nature of eval... a little tricky. ;)


    Dave

Re: How to do that with eval ?
by perlmonkey (Hermit) on Apr 10, 2004 at 07:43 UTC
    Something like this is probably more what you are looking for:
    my $sop = ... my $sport = ... while( <FH> ) { # get the 9th element my $port_a = (split /:/)[8]; # do the eval and capture the return value of comparison my $result = eval "\$port_a $sop \$sport"; # exit if eval failed die "eval error: $@" if $@; if( $result ) { # do something } else { # do something } }
    You have to escape the variables you dont want eval to replace (string interpolate). And the "if" statement is redunant. You can just return the boolean value of the comparison.
      $sop = ";print qq!You have been hacked rm -rf *!;";

      String eval + User Input == Shudder. Any. Arbitrary. Code.

      cheers

      tachyon

        Absolutely. If the above script was a cgi, you might as well reformat your disk now. If the script is meant to be a 'quick hack', evals work, they work well, and with minimal coding.

        I have used unsafe hacks like this for my personal dirty scripts because I dont have the time, inclination, or need to make them luser or h4x0r safe.

        As tachyon says, never use eval on user input unless this is intended to be a 'dirty quick hack'.
Re: How to do that with eval ?
by ysth (Canon) on Apr 11, 2004 at 05:59 UTC
    In general, if you have problems with eval, make sure you print the error message in $@ if the eval fails along with the string you are evaling; that will usually help pinpoint the error.

    In designing the code in the first place, instead of randomly tossing together "" or '' and $variables, decide what you want the evaled code to look like for given input and then write the code to produce that string, paying attention to the different interpolation rules of '' or "".