Eval

thank you

Any substitution you haven't escaped with \ happens before you run eval, just as if you'd passed that string into anything else. Only after it does that, will it evaluate the string as raw shell syntax.

As an aside, eval is generally to be avoided.

Out of curiosity, why is eval considered evil :mad:. The hits I find with an internet search are pretty much all java & perl, so what is a problem to ksh?

I try to avoid it, but there are occasions where I can't find another sensible way. I understand the cost of another process, especially if it's used in a loop, but is it dangerous.

Thanks, in advance,
Robin

Because it will evaluate any shell syntax you put into it, even things you didn't intend it to.

Imagine your program prompts for a user name, and someone types in $(rm -Rf ~/) . Then that variable gets fed into an eval...

It is very, very difficult to make eval secure from this. Not impossible, but very hard. Much doublethink is required.

Further, it's often used by beginning programmers as a bridge or shoehorn when they don't know a better way to solve a problem.

1 Like

I feel such a fool. Fortunately, I only use it to get an indirect variable or scheduled tasks reading in assignments from a parameter file that I control, which is a pretty rare need in itself.

Things such as:-

b=a
c=`eval echo $"{\`echo $b\`}"`

or

eval `grep "^GLOBAL:" $parmfile|cut -f2 -d":"`
eval `grep "^$JOBNAME:" $parmfile|cut -f2- -d":"`

.... where $parmfile contains statements such as:-

GLOBAL:COBRUN=cobrun
VALREP:VALREP.04146
QUOTES:COBRUN=qcobrun
VALUATION:export dd_REPORT=`grep "^VALREP:" $parmfile|cut -f2- -d":"`;echo dd_REPORT

I must admit that the parameter file is very closely guarded, but I agree it is susceptible to what you suggest.

I'm a little worried now...... Time to think a bit harder for older ksh. Any suggestions of a better way are very welcome.

Robin

In BASH, you can get an indirect variable via

NAME="VAR"
VAR="ASDF"
echo "${!VAR}"

This is a bash-only feature sadly, I used to think ksh93 had it too..

You can set arbitrary variable names with the read builtin, which will work in any shell. This trick works because read takes a variable name, not the variable itself.

VARNAME="ASDF"
read $VARNAME <<EOF
this contents get assigned to asdf
EOF

echo $ASDF
1 Like

Hi Robin,
The eval command isn't evil, it is just dangerous to use it to evaluate any user supplied text. In a script, it can be used safely to evaluate commands when the command is entirely under the control of the script writer. For example to get the value of the last two command line arguments passed to a shell script, save the following in a file named last2 :

#!/bin/ksh 
echo "${0##*/} has been called with $# arguments."
if [ $# -gt 1 ]
then    eval lm1=\$$(($# - 1))
else    lm1="There is only one argument."
fi
eval last=\$$# 
printf "The last two arguments are \"%s\" and \"%s\"\n" "$lm1" "$last"

and make it executable:

chmod +x last2

Then the command:

./last2 * '$(echo rm -rf ~loginID)'

will print:

last2 has been called with 9 arguments.
The last two arguments are "zebra" and "$(echo rm -rf ~loginID)"

if there are 7 files in the current directory and the last one (sorted alphabetically) is named zebra .

As Corona688 has already shown, if the script were then to use eval on the user supplied input stored in $last , the results could ruin the day for the user with the login name loginID!

Hi Corona688,
There are name reference variables in ksh93, but the syntax is different. In ksh you have to explictly state that a variable is to be treated as a reference variable using either the nameref or the typeset -n command. They can be used as shown here:

#!/bin/ksh
set -xv
x=123
nameref y=x
echo $y
echo ${!y}
y=456
echo $x
typeset -n z=x
echo $z
z="hello world"
echo $x

which produces the following combined stdout and stderr output:

x=123
+ x=123
nameref y=x
+ y=x
+ typeset -n y
echo $y
+ echo 123
123
echo ${!y}
+ echo x
x
y=456
+ x=456
echo $x
+ echo 456
456
typeset -n z=x
+ z=x
+ typeset -n z
echo $z
+ echo 456
456
z="hello world"
+ x='hello world'
echo $x
+ echo hello world
hello world
3 Likes

thank you

It depends what you have defined for the variable var, which appears to be a file name. Would you be trying to effectively call in some other code here? What's wrong with just making the file executable and running it, i.e.

echo "About to run my sub-script in $var"
$var
echo "Finished running sub-script in $var"

Apologies if I have misunderstood, but you may just be over thinking your puzzle.

Of course, if you are setting values in your sub-script that you want to remain for your calling script, you would code it like this (for ksh):-

echo "About to run my sub-script in $var"
. $var
echo "Finished running sub-script in $var"

Robin

thank you

Why not just source it? . /path/to/filename

This runs it inside your own shell line-by-line.

thank you

Why not source the temp file, then?

It's not a question of how eval works, it's a question of how the shell works. When a string is unquoted, it splits on all whitespace, not lines.

Using eval means the string gets split twice -- once when you feed it into eval, and again when eval actually executes the code you give it.

What is the echo for? Do you want eval to print those statements or run them?

I think eval "$(cat file)" is closest to what you want... But again I strongly question whether you need eval at all. I bet you could have avoided it if you'd known that using a pipe there would paint you into a corner.

I thought I knew what you were trying to do, but now I'm completely lost. In your original message you said you wanted to know the difference between:

eval echo "`cat $var`"

and:

eval echo "\"`cat $var`\""

There is a very easy way to see the difference. Set var to be the name of a file that contains:

echo "this is step 1"
date

and then run the commands. In the first case, the output will be:

echo this is step 1
Thu Apr 25 11:47:54 PDT 2013

modified to be the date corresponding to when you run it and your setting of the TZ environment variable. In the second case, the output will be:

echo this is step 1
date

So, the difference is that in one, the date command is executed and in the other it is not. And this is explained by the fact that you escaped one set of double quotes in the 2nd eval.

But, this has nothing to do with pipes nor directing the output to a temp file. You can redirect or pipe the output of an eval command and you can redirect or pipe the output of a source (.) command the same way.

I think you need to restate what you are trying to do with pipes and temp files, show us what you're doing, show us what results you're getting, and show or tell us how the results you want are different from what you're getting.

thank you

No it isn't difficult. With:

eval echo "`cat $var`"

the eval processes the command line yielding:

echo echo "this is step 1"
date

and then passes those commands to the shell yielding:

echo this is step 1
<<<output from date command>>>

but with:

eval echo "\"`cat $var`\""

the eval processes the command line yielding:

echo "echo "this is step 1"
date"

and then passes this single echo command to the shell yielding:

echo this is step 1
date

The escaped quotes from this eval and the quotes inside file were paired together by the shell just as it would if you typed the command:

echo "echo "this is step 1"
date"

into your shell.

thank you

All of the descriptions I've seen of eval are vague (including in the formal standards) and just say something like:

From years of experience, I believe that what eval does when concatenating arguments is to perform alias substitution, tilde expansion, parameter expansion, command substitution, arithmetic expansion, field splitting, pathname expansion, quote removal, and (maybe) I/O redirections. (I've never had a case when using eval where it mattered whether redirections happened on the first command line evaluation or were only evaluated when eval passed the resulting command to the shell for its second evaluation, so I've never needed to try to construct a case to determine when redirections are performed.)

The easy way to see what eval does is to issue the command:

set -xv

and type some eval commands into your shell. (When you're done with this experiment, enter the command:

set +xv

to turn off input echoing and tracing.)

thank you

If you read my post, you will find a syntax for eval that works in it...