The internal eval command interprets and expands a command line before the shell interprets and expands the line. Essentially it permits dynamically construct program or statements and then execute them. This is a feature typical for all scripting
languages and one of the most powerful one. Among other things you can with it we can mention the following:
- Execute a command line you read using the read command.
- Find the value of a variable whose name is derived from the value of another variable.
- To repeat a stage or portion of command processing.
- To apply results of expansions to earlier stages of the expansion.
The eval command similar to dot command. Dot command is mainly used for execution of static code. Eval is used for execution dynamically generated code.
The general format of the eval command is very simple:
eval [ command_to_be_interpreted... ]
No options whatsoever which is pretty rare. The shell expands arguments to
eval using standard command processing rules. Then the shell forms a space-separated string of all the arguments. The shell reads the string as a command line and processes it again and executes it.
The following example illustrates how you can use eval to expand variables within a command line to be the name of another variable.
eval last_arg='$'{$#} # expands to last positional parameter
Here is another pretty artificial, but still useful for understanding the eval command example:
X=10 Y=X echo '$'$Y eval echo '$'$Y 10
Before most of Unix shells got one-dimensional arrays, one of the most popular use of the
eval command was, probably, imitation of arrays in shell:
The command:
cat /etc/group | while read line do read line eval x$a="$line" donesets variables x1, x2 , x3, and so on to the lines read. . Once this has been done, you can use variables
$x1 $x2 and so on in any shell construct, or construct them dynamically to get data, for example:for a in 3 2 1 do eval line=x$a; echo $line doneproduces the lines in different order
See also
Example 15-12 ( below). Again the key idea if dynamic "on the fly" generation of code. Good, practical examples of this "on the fly code generation" usually are pretty complex and the best simple example that I have found is an example from O'Reilly book
Learning the Korn shell:
we constructed a simple pipeline that sorts a file and prints out the first
N lines, where N defaults to 10. The resulting pipeline was:sort -nr $1 | head -${2:-10}The first argument specified the file to sort; $2 is the number of lines to print.
Now suppose we change the task just a bit so that the default is to print the
entire file instead of 10 lines.This means that we don't want to usehead at all in the default case. We could do this in the following way:if [[ -n $2 ]]; then sort -nr $1 | head -$2 else sort -nr $1 fiIn other words, we decide which pipeline to run according to whether or not
$2 is null. But here is a more compact solution:eval sort -nr \$1 ${2:+"| head -\$2"}The last expression in this line evaluates to the string | head -\$2 if
$2 exists (is not null); if $2 is null, then the expression is null too. We backslash-escape dollar signs (\$) before variable names to prevent unpredictable results if the variables' values contain special
characters like > or |. The backslash effectively puts off the variables' evaluation until the
eval command itself runs. So the entire line is either:eval sort -nr \$1 | head -\$2if $2 is given or:
eval sort -nr \$1if $2 is null. Once again, we can't just run this command without
eval because the pipe is "uncovered" after the shell tries to break the line up into commands.
eval causes the shell to run the correct pipeline when $2 is given.
Examples
Example 1: Performing Variable Indirection
This example uses the eval utility to perform variable indirection. The first part of this example shows what happens when the
eval utility is not used. The value
10 is assigned to the variable named
abc and the value
abc is assigned to the variable named
x. Next, the variable named
y is assigned the value of the variable
x prefixed by
$ or, in this case, the value
$abc.
abc=10 x=abc y='$'$x echo $y
Theecho command produces the following output:
$abc
You can instead use the eval to perform the assignment to the variable named
y, as in:
eval y='$'$x echo $y
In this case, the eval utility expands the assignment toy=$abc and passes
that to the shell for further expansion, resulting iny having the value of the variable named
abc. This means that theecho command produces
the following output:
10
Setting and Printing Values of Arbitrary Variables
#!/bin/sh echo "Enter variable name and value separated by a space" read VARIABLE VALUE echo Assigning the value $VALUE to variable $VARIABLE eval $VARIABLE=$VALUE # print the value eval echo "$"$VARIABLE # export the value eval export $VARIABLE # print the exported variables. export
This example takes user input, constructs a variable based on the value entered using eval, then prints the value stored in the resulting variable.
Checking the last parameter in the list
set One Two Three Four if eval [ ! -f \${$#} ] then echo "$4: last argument must be a file!" exit 1 fi
This code checks for the last argument on the command line to be the name of a file that exists. The first line sets the positional parameters to One Two Three Four. Since Four is not the name of a file, your code will return the message about the last file.
The exit is commented out so you are not logged off when the command executes.
Tuesday's Tips for Unix Shell Scripts
The
randstr
function selects one of its arguments at random and puts it in the variable $_RETVAL:randstr() { [ $# -eq 0 ] && return 1 n=$(( ($RANDOM % $#) + 1 )) eval _RETVAL=\${$n} }For example, to pick a card at random:
randstr diamonds hearts clubs spades suit=$_RETVAL randstr Ace 2 3 4 5 6 7 8 9 10 Jack Queen King card="$_RETVAL of $suit" echo $card
Examples from Advanced Bash-Scripting Guide (Internal Commands and Builtins):
- eval
- eval arg1 [arg2] ... [argN]
Combines the arguments in an expression or list of expressions and evaluates them. Any variables within the expression are expanded. The net result is to
convert a string into a command.
The eval command can be used for code generation from the command-line or within a script.
bash$ command_string="ps ax" bash$ process="ps ax" bash$ eval "$command_string" | grep "$process" 26973 pts/3 R+ 0:00 grep --color ps ax 26974 pts/3 R+ 0:00 ps ax
Each invocation of eval forces a re-evaluation of its arguments.
a='$b' b='$c' c=d echo $a # $b # First level. eval echo $a # $c # Second level. eval eval echo $a # d # Third level. # Thank you, E. Choroba.
Example 15-11. Showing the effect of eval
#!/bin/bash # Exercising "eval" ... y=`eval ls -l` # Similar to y=`ls -l` echo $y #+ but linefeeds removed because "echoed" variable is unquoted. echo echo "$y" # Linefeeds preserved when variable is quoted. echo; echo y=`eval df` # Similar to y=`df` echo $y #+ but linefeeds removed. # When LF's not preserved, it may make it easier to parse output, #+ using utilities such as "awk". echo echo "===========================================================" echo eval "`seq 3 | sed -e 's/.*/echo var&=ABCDEFGHIJ/'`" # var1=ABCDEFGHIJ # var2=ABCDEFGHIJ # var3=ABCDEFGHIJ echo echo "===========================================================" echo # Now, showing how to do something useful with "eval" . . . # (Thank you, E. Choroba!) version=3.4 # Can we split the version into major and minor #+ part in one command? echo "version = $version" eval major=${version/./;minor=} # Replaces '.' in version by ';minor=' # The substitution yields '3; minor=4' #+ so eval does minor=4, major=3 echo Major: $major, minor: $minor # Major: 3, minor: 4Example 15-12. Using eval to select among variables
#!/bin/bash # arr-choice.sh # Passing arguments to a function to select #+ one particular variable out of a group. arr0=( 10 11 12 13 14 15 ) arr1=( 20 21 22 23 24 25 ) arr2=( 30 31 32 33 34 35 ) # 0 1 2 3 4 5 Element number (zero-indexed) choose_array () { eval array_member=\${arr${array_number}[element_number]} # ^ ^^^^^^^^^^^^ # Using eval to construct the name of a variable, #+ in this particular case, an array name. echo "Element $element_number of array $array_number is $array_member" } # Function can be rewritten to take parameters. array_number=0 # First array. element_number=3 choose_array # 13 array_number=2 # Third array. element_number=4 choose_array # 34 array_number=3 # Null array (arr3 not allocated). element_number=4 choose_array # (null) # Thank you, Antonio Macchi, for pointing this out.Example 15-13. Echoing the command-line parameters
#!/bin/bash # echo-params.sh # Call this script with a few command-line parameters. # For example: # sh echo-params.sh first second third fourth fifth params=$# # Number of command-line parameters. param=1 # Start at first command-line param. while [ "$param" -le "$params" ] do echo -n "Command-line parameter " echo -n \$$param # Gives only the *name* of variable. # ^^^ # $1, $2, $3, etc. # Why? # \$ escapes the first "$" #+ so it echoes literally, #+ and $param dereferences "$param" . . . #+ . . . as expected. echo -n " = " eval echo \$$param # Gives the *value* of variable. # ^^^^ ^^^ # The "eval" forces the *evaluation* #+ of \$$ #+ as an indirect variable reference. (( param ++ )) # On to the next. done exit $? # ================================================= $ sh echo-params.sh first second third fourth fifth Command-line parameter $1 = first Command-line parameter $2 = second Command-line parameter $3 = third Command-line parameter $4 = fourth Command-line parameter $5 = fifthExample 15-14. Forcing a log-off
#!/bin/bash # Killing ppp to force a log-off. # For dialup connection, of course. # Script should be run as root user. SERPORT=ttyS3 # Depending on the hardware and even the kernel version, #+ the modem port on your machine may be different -- #+ /dev/ttyS1 or /dev/ttyS2. killppp="eval kill -9 `ps ax | awk '/ppp/ { print $1 }'`" # -------- process ID of ppp ------- $killppp # This variable is now a command. # The following operations must be done as root user. chmod 666 /dev/$SERPORT # Restore r+w permissions, or else what? # Since doing a SIGKILL on ppp changed the permissions on the serial port, #+ we restore permissions to previous state. rm /var/lock/LCK..$SERPORT # Remove the serial port lock file. Why? exit $? # Exercises: # --------- # 1) Have script check whether root user is invoking it. # 2) Do a check on whether the process to be killed #+ is actually running before attempting to kill it. # 3) Write an alternate version of this script based on 'fuser': #+ if [ fuser -s /dev/modem ]; then . . .Example 15-15. A version of rot13
#!/bin/bash # A version of "rot13" using 'eval'. # Compare to "rot13.sh" example. setvar_rot_13() # "rot13" scrambling { local varname=$1 varvalue=$2 eval $varname='$(echo "$varvalue" | tr a-z n-za-m)' } setvar_rot_13 var "foobar" # Run "foobar" through rot13. echo $var # sbbone setvar_rot_13 var "$var" # Run "sbbone" through rot13. # Back to original variable. echo $var # foobar # This example by Stephane Chazelas. # Modified by document author. exit 0The eval command occurs in the older version ofindirect referencing.
eval var=\$$var
The eval command can be risky, and normally should be avoided when there exists a reasonable alternative. An
eval $COMMANDS executes the contents of COMMANDS, which may contain such unpleasant surprises as
rm -rf *. Running an eval on unfamiliar code written by persons unknown is living dangerously.
Using the eval Builtin for Data Structures, Arrays, and Indirection
One of the more under-appreciated commands in shell scripting is the
eval
builtin. The
eval
builtin takes a series of arguments, concatenates them into a single command, then executes it.For example, the following script assigns the value
3
to the variable
X
and then prints the value:#!/bin/sheval X=3echo $XFor such simple examples, the
eval
builtin is superfluous. However, the behavior of the
eval
builtin becomes much more interesting when you need to construct or choose variable names programmatically. For example, the next script also assigns the value3
to the variable
X
:#!/bin/shVARIABLE="X"eval $VARIABLE=3echo $XWhen the
eval
builtin evaluates its arguments, it does so in two steps. In the first step, variables are replaced by their values. In the preceding example, the letter
X
is inserted in place of$VARIABLE
. Thus, the result of the first step is the following string:X=3In the second step, the
eval
builtin executes the statement generated by the first step, thus assigning the value
3
to the variableX
. As further proof, theecho
statement at the end of the script prints the value
3
.The
eval
builtin can be particularly convenient as a substitute for arrays in shell script programming. It can also be used to provide a level of indirection, much like pointers in C. Some examples of the
eval
builtin are included in the sections that follow.A Complex Example: Setting and Printing Values of Arbitrary Variables
The next example takes user input, constructs a variable based on the value entered using
eval
, then prints the value stored in the resulting variable.#!/bin/shecho "Enter variable name and value separated by a space"read VARIABLE VALUEecho Assigning the value $VALUE to variable $VARIABLEeval $VARIABLE=$VALUE# print the valueeval echo "$"$VARIABLE# export the valueeval export $VARIABLE# print the exported variables.exportWarning: This script executes arbitrary user input. It is intended
only as an example of the usage of theeval
builtin. In real-world code, you should
never pass unsanitized user input directly toeval
because doing so can provide a vector for arbitrary code execution.
Run this script and type something like
MYVAR 33
. The script assigns the value
33
to the variableMYVAR
(or whatever variable name you entered).You should notice that the echo command has an additional dollar sign (
$
) in quotes. The first time the
eval
builtin parses the string, the quoted dollar sign is simplified to merely a dollar sign. You could also surround this dollar sign with single quotes or quote it with a backslash, as described in“Quoting
Special Characters.” The result is the same.Thus, the statement:
eval echo "$"$VARIABLEevaluates to:
echo $MYVARNote: If you forget to quote the first dollar sign, you get a very strange result. The variable
$$
is a special shell variable that contains the process ID of the current shell. Thus, without quoting the first dollar sign, the two dollar signs are interpreted as a variable, and thus the statement evaluates to something like:
echo 1492MYVARThis is probably not what you want.
A Practical Example: Using eval to Simulate an Array
In“Shell Variables and Printing,” you learned how to read variables from standard
input. This was limited to some degree by the inability to read an unknown number of user-entered values.The script below solves this problem using
eval
by creating a series of variables to hold
the values of a simulated array.#!/bin/shCOUNTER=0VALUE="-1"echo "Enter a series of lines of test. Enter a blank line to end."while [ "x$VALUE" != "x" ] ; doread VALUEeval ARRAY_$COUNTER=$VALUEeval export ARRAY_$COUNTERCOUNTER=$(expr $COUNTER '+' 1) # More on this in Paint by NumbersdoneCOUNTER=$(expr $COUNTER '-' 1) # Subtract one for the blank value at the end.# print the exported variables.COUNTERB=0;echo "Printing values."while [ $COUNTERB -lt $COUNTER ] ; doecho "ARRAY[$COUNTERB] = $(eval echo "$"ARRAY_$COUNTERB)"COUNTERB=$(expr $COUNTERB '+' 1) # More on this in Paint by NumbersdoneThis same technique can be used for splitting an unknown number of input values in a single line as shown in the next listing:
#!/bin/shCOUNTER=0VALUE="-1"echo "Enter a series of lines of numbers separated by spaces."read LISTIFS=" "for VALUE in $LIST ; doeval ARRAY_$COUNTER=$VALUEeval export ARRAY_$COUNTERCOUNTER=$(expr $COUNTER '+' 1) # More on this in Paint by Numbersdone# print the exported variables.COUNTERB=0;echo "Printing values."while [ $COUNTERB -lt $COUNTER ] ; doecho "ARRAY[$COUNTERB] = $(eval echo '$'ARRAY_$COUNTERB)"COUNTERB=$(expr $COUNTERB '+' 1) # More on this in Paint by NumbersdoneA Data Structure Example: Linked Lists
In a complex shell script, you may need to keep track of multiple pieces of data and treat them like a data structure. The
eval
builtin makes this easy. Your code needs to pass around only a single name from which you
build other variable names to represent fields in the structure.Similarly, you can use the
eval
builtin to provide a level of indirection similar to pointers in C.For example, the following script manually constructs a linked list with three items, then walks the list:
#!/bin/shVAR1_VALUE="7"VAR1_NEXT="VAR2"VAR2_VALUE="11"VAR2_NEXT="VAR3"VAR3_VALUE="42"HEAD="VAR1"POS=$HEADwhile [ "x$POS" != "x" ] ; doecho "POS: $POS"VALUE="$(eval echo '$'$POS'_VALUE')"echo "VALUE: $VALUE"POS="$(eval echo '$'$POS'_NEXT')"doneUsing this technique, you could conceivably construct any data structure that you need (with the caveat that manipulating large data structures in shell scripts is generally not conducive to good performance).