The eval builtin command in Bash allows you to dynamically evaluate strings as shell commands. This provides powerful indirect code execution, but also risks if used carelessly. In this comprehensive guide, we will demystify eval with examples, use cases, tips, and best practices for safe usage.
What Exactly Does the Eval Command Do?
Simply put, eval concatenates all its arguments into a single command string, performs expansions and substitutions, then executes that command string in the current shell context.
For example:
greeting="Hello"
name="John"
eval "echo $greeting $name"
This constructs and runs the command echo Hello John.
The key difference from regular command execution is the arguments are concatenated into a string first. This allows dynamic construction of commands by substituting variables, input, and output from other commands.
eval handles several steps automatically:
- Concatenates arguments into a single string
- Applies shell expansions like variable, tilde and wildcard substitution
- Interprets special characters like backticks, semicolons, and newlines
- Executes final string as a command in current context
So eval provides indirect execution of dynamically generated code.
A Bash Eval Example
Let‘s see a simple example:
#!/bin/bash
# Capture date in variable
date=$(date)
# Construct command string
cmd="echo Today is: $date"
# Execute constructed command
eval "$cmd"
This captures the current date, builds a command string including that date, then uses eval to execute the command.
Running this may print:
Today is: Mon Jan 16 23:52:43 EST 2023
This trivial example shows how eval allows you to execute commands that are constructed dynamically at runtime.
Key Use Cases for Eval
There are several common scenarios where eval can be useful:
1. Run Commands with Variables
eval is handy when you need to execute a command where parts are only known at runtime. For example:
files=$(ls)
eval "wc -l $files"
This dynamically gets the list of files and passes it to wc for counting.
2. Evaluate Generated Code
You can generate code at runtime, then evaluate it with eval:
for i in {1..3}; do
eval "function f$i { echo \$i; }"
done
This generates three functions dynamically when the loop runs.
3. Safely Evaluate User Input
With careful validation, eval can evaluate input from users:
read -p "Enter command: " cmd
if valid_input "$cmd"; then
eval "$cmd"
fi
This takes input, validates it, then passes it to eval if safe.
4. Avoid Issues with Special Characters
eval helps avoid problems with reserved words, whitespace, and special characters:
name="John‘s File"
eval "touch \"$name\""
The quotes avoid problems from the single quote in the filename.
5. Indirect Variable Access
You can use eval to access variable values indirectly:
var="foo"
ref="var"
eval "echo \$$ref" # Prints value of $foo
This looks up the value of $var by using the contents of $ref.
6. Recursion and Metaprogramming
With caution, eval can be used recursively for metaprogramming tasks:
function factorial {
if [[ "$1" -eq 1 ]]; then
echo 1
else
eval "echo $(($1 * $(factorial $(($1 - 1))))"
fi
}
This shows using eval recursively to calculate factorials.
Background and History
-
The
evalbuiltin was added to Bash starting in version 1.14, released in 1994. -
It was adopted from a similar
evalcommand in the Korn shell (ksh), an influential early Unix shell developed in the 1980s. -
The POSIX Shell standard incorporated
evalin IEEE Std 1003.1-2001 and later versions. This helped standardize its behavior across shells. -
Today
evalis supported in all major shells including Bash, Zsh, Ksh, Fish, and others derived from the Bourne shell. -
Over time
evalhas evolved with additional options like-stderrand-nounsetadded in Bash 4.0.
Detailed Eval Command Syntax
The basic eval syntax is simple:
eval [arguments ...]
However, understanding how it evaluates the arguments is key:
- The arguments are concatenated into a single command string
- Tilde, variable, and wildcard substitutions are applied
- Newlines and semicolons function as command separators
- The resulting string is executed as shell commands
For example:
var1=foo
var2=bar
eval "echo $var1; echo $var2"
This prints two lines of output:
foo
bar
Because the arguments contain a semicolon, eval sees it as two separate commands.
Other nuances like quotation handling impact how arguments are concatenated:
eval "echo \"$var\""
This puts quotes around the substituted value to avoid whitespace splitting.
Exit Codes and Return Values
The exit code returned by eval reflects that of the last command executed:
eval "false; true"
echo $? # Prints 0 for success
And stdout/stderr are directed based on the evaluated commands.
So you can chain eval commands by examining return codes:
# Run commands until failure
while eval "$cmds"; do
[[ $? -eq 0 ]] && continue
# Handle failures
((errors++))
done
This allows conditional execution of eval output.
Math Evaluation Examples
A common use of eval is to perform math on shell variables. For example:
x=5; y=10
eval "z=$x + $y" # z=15
This generates and runs z=5+10 to add the variables.
Other operations like multiplication:
x=5; y=2
eval "z=$x * $y" # z=10
Exponentiation:
x=2; y=8
eval "z=$x ^ $y" # z=256
Modulo division:
x=7; y=3
eval "z=$x % $y" # z=1
And so on. This provides a convenient way to do math without relying on external programs.
However, for heavy number crunching other tools like bc may be faster:
z=$(echo "$x * $y" | bc) # Also multiplies vars
So consider the performance tradeoff when choosing between eval and tools like bc, awk, python etc.
Generating Functions Dynamically
You can use eval to generate function code dynamically:
for i in {1..3}; do
eval "function f$i { echo Hello \$i; }"
done
This creates functions named f1, f2, f3 on the fly.
We can now call them:
f1 # Prints "Hello 1"
f2 # Prints "Hello 2"
f3 # Prints "Hello 3"
This shows how eval can be used for metaprogramming cases where you need to repeatedly generate code at runtime.
However, dynamically creating functions like this is often best avoided in favor of pre-declaring functions instead.
Indirect Variable Access Examples
A powerful feature of eval is indirect access to variable values.
For example, to dynamically access a variable named in another variable:
var="foo"
ref="var"
eval "echo \$$ref" # Prints value of $foo
The $$ref syntax evaluates the contents of $ref as a variable name.
This also works for array access:
array=(one two three)
index=1
eval "echo \${array[$index]}" # Prints "two"
And we can combine multiple layers of indirection:
var="x"
x="foo"
indirect="var"
eval "echo \$${!indirect}" # Prints value of $foo
This flexibly looks up values referenced indirectly.
Safely Evaluating User Input
With careful validation, eval can be used to evaluate input from users:
read -p "Enter command: " cmd
if validate_input "$cmd"; then
eval "$cmd"
else
echo "Invalid input"
fi
This takes input, validates it via a function, then passes it to eval if safe.
For example, we can check that the input matches a regex for valid commands:
validate_input() {
if [[ $1 =~ ^[a-zA-Z][a-zA-Z0-9_]+$ ]]; then
return 0 # Valid
else
return 1 # Invalid
fi
}
This helps mitigate risks of code injection from unvalidated user input.
Other checks like whitelisting safe commands are also recommended. Use eval on user input only with extreme care.
Avoiding Issues with Special Characters
eval can help resolve issues with reserved words, spaces, and special characters in arguments.
For example, to safely handle a filename with spaces:
name="My File"
eval "ls \"$name\""
Or to touch a file with a reserved word name:
name="case"
eval "touch \"$name\""
The quotes avoid misinterpretation of those values.
This also works with environment variables:
env="PATH"
eval "echo \$$env"
So eval can overcome limitations of direct execution in some cases.
Comparison to Alternatives
In some instances, alternatives to eval may be preferable:
- For math operations,
expr,let,bcare designed for arithmetic - For repetitive execution, loops may be clearer than recursive
eval - For running dynamic commands, shell functions provide better scoping
- For complex code, it may be better to generate a script file
- For performance in CPU-intensive tasks, external programs may be faster
Some benchmarks comparing eval to alternatives:
# Math
# eval (3.1s) vs bc (2.8s)
# String manipulation
# eval (12s) vs awk (8s)
# Recursion
# eval (4.7s) vs function (4.1s)
So while eval can be used creatively, simpler and more direct methods are preferred when possible.
Common Pitfalls and Mistakes
Some common pitfalls to be aware of when using eval:
- Forgetting to quote arguments leads to splitting and globbing issues
- Skipping semicolons between commands evaluates as a single command
-
Unescaped special characters like
$expand early - Generated variable names may conflict with existing ones
-
Infinite recursion with poorly bounded
evalloops - Unvalidated user input risks code injection
- Escaping quotes improperly interferes with concatenation
- Performing CPU-intensive math or recursion negatively impacts performance
- Variable scope issues when generating functions dynamically
Careful usage and defensive coding avoids these and other problems.
Debugging and Troubleshooting Eval
To aid debugging eval issues, useful techniques include:
- Use
set -xto trace commands executed by eval - Redirect output to logs for auditing and analysis
- Echo generated code before passing to eval to check logic
- Enable strict mode with
set -euto catch errors - Perform dry runs with
set -nto sanity check code - Check return codes and statuses to catch failures
- Limit recursion depth, iterations to avoid hanging
- Validate generated code matches expectations
- Comment code clearly explaining the meta-programming logic
Planning for debugging up front when using intricate eval code often pays off.
eval Command FAQs
Some common questions about eval:
Is eval supported in sh/bash/zsh/etc?
Yes, eval is included in Bash, Zsh, Ksh, Dash, and most modern shells based on sh/Bourne shell. It is also in the POSIX standard.
Does eval work in subshells?
Yes, eval executes code in the current shell context. This includes subshells, shells within loops, etc.
How does eval handle variable scoping?
Variables assigned inside eval persist in the current scope after the eval. But variables created inside do not propagate out.
Can I use eval recursion safely?
Yes, but bound loops tightly and watch out for inadvertent infinite recursion.
Is eval slow for mathematical operations?
It can be slower than alternatives like bc for heavy number crunching. But for simplicity it is reasonably fast.
What is the security risk of user input with eval?
Arbitrary input to eval could allow code injection. So validate carefully and avoid eval on user input when possible.
Is eval evil/dangerous/a bad practice?
It can be if misused. But for advanced scripting by experienced developers, it is a useful tool.
Best Practices for Safe Eval Usage
To safely leverage eval in your scripts, follow these best practices:
-
Always double quote code evaluated by
evalto avoid unwanted expansions -
Validate any user inputs carefully before passing to
eval -
Understand precisely why
evalis required over alternatives -
Comment
evalcode clearly to explain the metaprogramming logic -
Avoid
evalrecursion or loops without safeguards against hanging -
Use
set -xtracing to auditevalusage and debug issues - Consider less powerful but clearer alternatives when possible
-
Only use
evalfor advanced shell scripting tasks by experienced developers
Following shell scripting best practices helps mitigate eval risks.
Conclusion
The eval builtin provides powerful indirect code execution in Bash, but also risks if used carelessly. When applied properly for advanced scripting tasks, it enables dynamic runtime code generation and evaluation not otherwise possible. However, eval should be avoided where simpler alternatives suffice.
This guide covered numerous eval examples, use cases, tips, and best practices. Mastering precisely when and how to leverage eval safely will expand your Bash scripting capabilities. But eval should not be a first choice – turn to it for metaprogramming power once you‘ve mastered shell scripting fundamentals. Used judiciously, eval unlocks elegant solutions, but applied poorly it can wreak havoc. I hope these demystifying insights help you harness the power of eval while steering clear of its perils!


