views:

2714

answers:

6

I would like to evaluate an environment variable and set the result to a variable:

$x=eval($ENV{EDITOR});
print $x;

outputs:

/bin/vi

works fine.

If I set an environment variable QUOTE to \' and try the same thing:

$x=eval($ENV{QUOTE});
print $x;

outputs:

(nothing)

$@ set to: "Can't find a string terminator anywhere before ..."

I do not wish to simply set $x=$ENV{QUOTE}; as the eval is also used to call a script and return its last value (very handy), so I would like to stick with the eval(); Note that all of the Environment variables eval'ed in this manner are set by me in a different place so I am not concerned with malicious access to the environment variables eval-ed in this way.

Suggestions?

+1  A: 

Well, you could double-escape the QUOTE's value, I guess, since you know that it's going to be evaled.

scraimer
That's like saying "you should take the antidote along with the poison you're about to drink". DON'T DRINK THE POISON!
Schwern
+11  A: 

Well, of course it does nothing.

If your ENV varaible contains text which is half code, but isn't and you give the resulting string to something that evaluates that code as Perl, of course it's not going to work.

You only have 3 options:

  1. Programmatically process the string so it doesn't have invalid syntax in it
  2. Manually make sure your ENV variables are not rubbish
  3. Find a solution not involving eval but gives the right result.

You may as well complain that

 $x = '

Is not valid code, because that's essentially what's occurring.

Samples of Fixing the value of 'QUOTE' to work

# Bad. 
QUOTE="'" perl -wWe 'print eval $ENV{QUOTE}; print "$@"'
#  Can't find string terminator "'" anywhere before EOF at (eval 1) line 1.

# Bad. 
QUOTE="\'" perl -wWe 'print eval $ENV{QUOTE}; print "$@"'
#  Can't find string terminator "'" anywhere before EOF at (eval 1) line 1.

# Bad. 
QUOTE="\\'" perl -wWe 'print eval $ENV{QUOTE}; print "$@"'
#  Can't find string terminator "'" anywhere before EOF at (eval 1) line 1.

# Good
QUOTE="'\''" perl -wWe 'print eval $ENV{QUOTE}; print "$@"' 
# '
Kent Fredric
That's all well and good, but he shouldn't be using eval() at all! It's incredibly dangerous and totally unnecessary to the task at hand. The OP clearly does not understand what it does.
Schwern
@Schwern I entirely agree, I don't recommend people use eval at all, and even if they think they know what they're doing, I usually assume they just haven't thought about it hard enough. But given answer is based on the fact I don't have a clue what he's actually doing with it.
Kent Fredric
+8  A: 

Why are you eval'ing in the first place? Should you just say

my $x = $ENV{QUOTE};
print "$x\n";

The eval is executing the string in $ENV{QUOTE} as if it were Perl code, which I certainly hope it isn't. That is why \ disappears. If you were to check the $@ variable you would find an error message like

syntax error at (eval 1) line 2, at EOF

If you environment variables are going to contain code that Perl should be executing then you should look into the Safe module. It allows you to control what sort of code can execute in an eval so you don't accidentally wind up executing something like "use File::Find; find sub{unlink $File::Find::file}, '.'"

Chas. Owens
+5  A: 

Evaluating an environment value is very dangerous, and would generate errors if running under taint mode.

# purposely broken
QUOTE='`rm system`'

$x=eval($ENV{QUOTE});
print $x;

Now just imagine if this script was running with root access, and was changed to actually delete the file system.

Brad Gilbert
+1  A: 

Maybe what you want is not Perl's eval but to evaluate the environment variable as the shell would. For this, you want to use backticks.

$x = `$ENV{QUOTE}`
nohat
Shouldn't this be $x = `echo -n \$QUOTE`;?
Chas. Owens
As its an environment variable presumably it's already been run through the shell so I have my doubts that this is what the OP is trying to do. I also can't fathom why you'd want to know how the shell will see a quote character.
Schwern
+2  A: 

Kent's answer, while technically correct, misses the point. The solution is not to use eval better, but to not use eval at all!

The crux of this problem seems to be in understanding what eval STRING does (there is eval BLOCK which is completely different despite having the same name). It takes a string and runs it as Perl code. 99.99% this is unnecessary and dangerous and results in spaghetti code and you absolutely should not be using it so early in your Perl programming career. You have found the gun in your dad's sock drawer. Discovering that it can blow holes in things you are now trying to use it to hang a poster. It's better to forget it exists, your code will be so much better for it.

$x = eval($ENV{EDITOR}); does not do what you think it does. I don't even have to know what you think it does, that you even used it there means you don't know. I also know that you're running with warnings off because Perl would have screamed at you for that. Why? Let's assume that EDITOR is set to /bin/vi. The above is equivalent to $x = /bin/vi which isn't even valid Perl code.

$ EDITOR=/bin/vi perl -we '$x=eval($ENV{EDITOR}); print $x'
Bareword found where operator expected at (eval 1) line 1, near "/bin/vi"
    (Missing operator before vi?)
Unquoted string "vi" may clash with future reserved word at (eval 1) line 2.
Use of uninitialized value $x in print at -e line 1.

I'm not sure how you got it to work in the first place. I suspect you left something out of your example. Maybe tweaking EDITOR until it worked?

You don't have to do anything magical to read an environment variable. Just $x = $ENV{EDITOR}. Done. $x is now /bin/vi as you wanted. It's just the same as $x = $y. Same thing with QUOTE.

$ QUOTE=\' perl -wle '$x=$ENV{QUOTE}; print $x'
'

Done.

Now, I suspect what you really want to do is run that editor and use that quote in some shell command. Am I right?

Schwern
+1, Of course, completely right, I just assumed that he was doing something special because we wern't seeing "real" ENV variables just over-simplified ones. Also, I didn't notice the warnings issue due to not running the code, so assuming that he has no quotes in his EDITOR, your answer is better
Kent Fredric