How to Use eval in Linux Bash Scripts

Linux laptop with a bash prompt
fatmawati ahmad zaenuri/Shutterstock.com

Of all the bash commands, poor old man eval probably has the worst reputation. Valid or just bad press? We discuss the uses and dangers of this least-loved Linux command.

We need to talk about eval

used carelessly, eval can lead to unpredictable behavior and even system insecurity. From the sound of it, we probably shouldn’t be using it, right? Not quite.

The same could be said about cars. In the wrong hands, they are a deadly weapon. People use them in ramming attacks and as getaway vehicles. Should we all give up the car? No of course not. But they have to be used properly and by people who know how to drive them.

The usual adjective applies to eval is “evil”. But it all depends on how it is used. That eval command sorts the Values from one or more variables. It creates a command string. It then executes this command. This makes it useful when you need to deal with situations where the content of a command is dynamically inferred during the execution of your script.

Problems arise when writing a script to use it eval on a string received from somewhere Outside the script. It can be entered by a user, sent via an API, tagged to an HTTPS request, or used elsewhere outside of the script.

If the string that eval will work, has not been derived locally and programmatically, there is a risk that the string contains embedded malicious instructions or other malformed input. Obviously you don’t want to eval execute malicious commands. So don’t use it to be on the safe side eval with externally generated strings or user input.

Getting started with eval

That eval The command is a built-in bash shell command. If bash is present, eval will be there.

eval concatenates its parameters into a single string. A single space is used to separate concatenated elements. It evaluates the arguments and then passes the entire string to the shell for execution.

Let’s create a variable called wordcount.

wordcount="wc -w raw-notes.md"

The String variable contains a word count command in a file called raw-notes.md.

we can use eval to run this command by passing it the value of the variables.

Using eval with a string variable to count the words in a file

The command runs in the current shell, not in a subshell. We can easily show that. We have a short text file called “variables.txt”. It contains these two lines.

first=How-To
second=Geek

We will use cat to send those lines to the terminal window. Then we will use eval evaluate a cat Command to run the instructions in the text file. This sets the variables for us.

cat variables.txt
eval "$(cat variables.txt)"
echo $first $second

Access to variables set by eval in the current shell

Through use echo To print the values ​​of the variables, we can see that eval The command runs in the current shell, not in a subshell.

A process in a subshell cannot change the shell environment of the parent process. Since eval runs in the current shell, the variables are set by eval can be used by the shell that started the eval Command.

Note that when you use eval in a script, the shell that would be changed eval is the subshell the script is running in, not the shell that started it.

TIED TOGETHER: How to use Linux cat and tac commands

Using variables in the command string

We can include other variables in the command strings. We will set two variables to contain integers.

num1=10 
num2=7

We create a variable that contains a expr Command that returns the sum of two numbers. This means that we need to access the values ​​of the two integer variables in the command. Notice the backticks around the expr Expression.

add="`expr $num1 + $num2`"

Let’s create another command to show us the result of expr Expression.

show="echo"

Note that we don’t need to add a space at the end of echo character string, still at the beginning of the expr Line. eval takes care of.

And to run the whole command we use:

eval $show $add

Using variables in the command string

The variable values ​​within the expr string are replaced by eval before passing it to the shell for execution.

TIED TOGETHER: How to work with variables in Bash

Access to variables within variables

You can assign a value to a variable and then assign the Surname this variable to another variable. Use evalyou can access it value held in the first variable, by their name, which is the value stored in the second variable. An example will help you unravel this.

Copy this script into an editor and save it as a file named assign.sh.

#!/bin/bash

title="How-To Geek"
webpage=title
command="echo"
eval $command \${$webpage}

We need to make it executable with the chmod Command.

chmod +x assign.sh

Using chmod to make a script executable

You must do this for all scripts that you copy from this article. Just use the appropriate script name in each case.

When we run our script, we see the text from the variable title although the eval command uses the variable webpage.

./assign.sh

Access a variable's value by its name, which is stored in another variable

The Escaped Dollar Sign”$” and the brackets “{}” causes eval to look at the value in the variable whose name is stored in webpage Variable.

Use dynamically created variables

we can use eval Create variables dynamically. This script is called “loop.sh”.

#!/bin/bash

total=0
label="Looping complete. Total:"

for n in {1..10}
do
  eval x$n=$n
  echo "Loop" $x$n
  ((total+=$x$n))
done

echo $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10

echo $label $total

It creates a variable called total which contains the sum of the values ​​of the variables we created. It then creates a string variable named label. This is a simple text string.

We will iterate through 10 loops and create 10 variables named x1 up to x10. That eval statement in the body of the loop returns the “x” and takes the value of the loop counter $n to create the variable name. At the same time, it sets the new variable to the value of the loop counter $n.

It prints the new variable in the terminal window and then increments the total Variable with the value of the new variable.

Outside the loop, the 10 new variables are printed again, all on one line. Note that we can also refer to the variables by their real names without using a computed or derived version of their names.

Finally, we print out the value of total Variable.

./loop.sh

Using eval to create variables dynamically

TIED TOGETHER: Primer: Bash loops: for, while and until

Using eval with arrays

Consider a scenario where you have a script that runs for a long time and does some processing for you. It writes to a log file with a name constructed from a timestamp. Occasionally a new log file is created. When the script finishes and there are no errors, it deletes the log files it created.

They don’t want it easy rm *.log, you just want it to delete the log files it creates. This script simulates this functionality. This is clear-logs.sh.

#!/bin/bash

declare -a logfiles

filecount=0 
rm_string="echo"

function create_logfile() {
  ((++filecount))
  filename=$(date +"%Y-%m-%d_%H-%M-%S").log
  logfiles[$filecount]=$filename
  echo $filecount "Created" ${logfiles[$filecount]}
}

# body of the script. Some processing is done here that
# periodically generates a log file. We'll simulate that
create_logfile
sleep 3
create_logfile
sleep 3
create_logfile
sleep 3
create_logfile

# are there any files to remove?
for ((file=1; file<=$filecount; file++))
do
  # remove the logfile
  eval $rm_string ${logfiles[$file]} "deleted..."
  logfiles[$file]=""
done

The script declares an array named logfiles . This contains the names of the log files created by the script. It declares a variable called filecount . This contains the number of log files created.

It also declares a string called rm_string. In a real script, this would include the rm command but we use echo This allows us to demonstrate the principle non-destructively.

The function create_logfile() This is where each log file is named and where it will be opened. We only create those filenameand pretend it was created in the file system.

The function increases the filecount Variable. Its initial value is zero, so the first filename we create will be stored at position one in the array. This is done on purpose, see later.

The file name is created with the date command and the .log extension. The name is stored in the array at the position specified by filecount. The name is displayed in the terminal window. In a real script, you would also create the actual file.

The main part of the script is simulated with sleep Command. It creates the first log file, waits three seconds, and then creates another. It creates four log files distributed in such a way that the timestamps in their filenames are different.

Finally there is a loop that deletes the log files. The loop counter file is set to one. It counts up to and including the value of filecountwhich contains the number of files created.

if filecount is still set to zero—because no log files were created—the loop body will never execute, since one is not less than or equal to zero. Therefore the filecount variable was set to zero at declaration and why it was incremented before The first file has been created.

Inside the loop we use eval with our non-destructive rm_string and the name of the file retrieved from the array. Then we set the array element to an empty string.

This is what we see when we run the script.

./clear-logs.sh

Deleting files whose names are stored in an array

It’s not all bad

Much slandered eval definitely has its uses. Like most tools, it is dangerous to use recklessly, and in more ways than one.

If you make sure the strings it’s working on are created internally and aren’t captured by humans, APIs, or things like HTTPS requests, you’ll avoid the biggest pitfalls.

TIED TOGETHER: How to show date and time in Linux terminal (and use it in bash scripts)

Leave a Reply

Your email address will not be published. Required fields are marked *