mirror of
https://github.com/rawiriblundell/wiki.bash-hackers.org
synced 2024-12-26 06:20:41 +01:00
171 lines
6.4 KiB
Markdown
171 lines
6.4 KiB
Markdown
|
# The eval builtin command
|
||
|
|
||
|
## Synopsis
|
||
|
|
||
|
eval: eval [arg ...]
|
||
|
|
||
|
## Description
|
||
|
|
||
|
`eval` takes its arguments, concatenates them separated by spaces, and
|
||
|
executes the resulting string as Bash code in the current execution
|
||
|
environment. `eval` in Bash works in essentially the same way as most
|
||
|
other languages that have an `eval` function. Perhaps the easiest way to
|
||
|
think about `eval` is that it works in the same way as running ''bash -c
|
||
|
"bash code..." ''from a script, except in the case of `eval`, the given
|
||
|
code is executed in the current shell environment rather than a child
|
||
|
process.
|
||
|
|
||
|
## Examples
|
||
|
|
||
|
In this example, the literal text within the
|
||
|
[here-document](/syntax/redirection#here_documents) is executed as Bash
|
||
|
code exactly as though it were to appear within the script in place of
|
||
|
the `eval` command below it.
|
||
|
|
||
|
#!/usr/bin/env bash
|
||
|
{ myCode=$(</dev/stdin); } <<\EOF
|
||
|
... arbitrary bash code here ...
|
||
|
EOF
|
||
|
|
||
|
eval "$myCode"
|
||
|
|
||
|
### Expansion side-effects
|
||
|
|
||
|
Frequently, `eval` is used to cause side-effects by performing a pass of
|
||
|
expansion on the code before executing the resulting string. This allows
|
||
|
for things that otherwise wouldn't be possible with ordinary Bash
|
||
|
syntax. This also, of course, makes `eval` the most powerful command in
|
||
|
all of shell programming (and in most other languages for that matter).
|
||
|
|
||
|
This code defines a set of identical functions using the supplied names.
|
||
|
`eval` is the only way to achieve this effect.
|
||
|
|
||
|
main() {
|
||
|
local fun='() { echo "$FUNCNAME"; }' x
|
||
|
|
||
|
for x in {f..n}; do
|
||
|
eval "${x}${fun}"
|
||
|
done
|
||
|
|
||
|
"$@"
|
||
|
}
|
||
|
|
||
|
main "$@"
|
||
|
|
||
|
### Using printf %q
|
||
|
|
||
|
The `printf %q` format string performs shell escaping on its arguments.
|
||
|
This makes `printf %q` the "anti-eval" - with each pass of a string
|
||
|
through printf requiring another `eval` to peel off the escaping again.
|
||
|
|
||
|
while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do
|
||
|
printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}"
|
||
|
done
|
||
|
$evalBall
|
||
|
|
||
|
The above example is mostly fun and games but illustrates the
|
||
|
`printf %q` property.
|
||
|
|
||
|
### Higher-order functions
|
||
|
|
||
|
Since all current POSIX-compatible shells lack support for [first-class
|
||
|
functions](http://en.wikipedia.org/wiki/First-class_function), it can be
|
||
|
tempting and sometimes useful to simulate some of their effect using
|
||
|
`eval` to evaluate a string containing code.
|
||
|
|
||
|
This example shows [partial
|
||
|
application](http://en.wikipedia.org/wiki/Partial_application) using
|
||
|
`eval`.
|
||
|
|
||
|
function partial {
|
||
|
eval shift 2 \; function "$1" \{ "$2" "$(printf '%q ' "${@:3}")" '"$@"; }'
|
||
|
}
|
||
|
|
||
|
function repeat {
|
||
|
[[ $1 == +([0-9]) ]] || return
|
||
|
typeset n
|
||
|
while ((n++ < $1)); do
|
||
|
"${@:2}"
|
||
|
done
|
||
|
}
|
||
|
|
||
|
partial print3 repeat 3 printf '%s ' # Create a new function named print3
|
||
|
print3 hi # Print "hi" 3 times
|
||
|
echo
|
||
|
|
||
|
This is very easy to do incorrectly and not usually considered idiomatic
|
||
|
of Bash if used extensively. However abstracting eval behind functions
|
||
|
that validate their input and/or make clear which input must be
|
||
|
controlled carefully by the caller is a good way to use it.
|
||
|
|
||
|
## Portability considerations
|
||
|
|
||
|
- Unfortunately, because eval is a **special builtin**, it only gets its
|
||
|
own environment in Bash, and only when Bash is not in POSIX mode. In
|
||
|
all other shells plus Bash in POSIX mode, the environment of eval will
|
||
|
leak out into the surrounding environment. It is possible to work
|
||
|
around this limitation by prefixing special builtins with the
|
||
|
`command` regular builtin, but current versions of ~~ksh93~~ and zsh
|
||
|
don't do this properly
|
||
|
([fixed](http://article.gmane.org/gmane.comp.programming.tools.ast.devel/686)
|
||
|
in ksh 93v- 2012-10-24 alpha). Earlier versions of zsh work (with
|
||
|
`setopt POSIX_BUILTINS` -- looks like a regression). This works
|
||
|
correctly in Bash POSIX mode, Dash, and mksh.
|
||
|
|
||
|
<!-- -->
|
||
|
|
||
|
- `eval` is another one of the few Bash builtins with keyword-like
|
||
|
conditional parsing of arguments that are in the form of compound
|
||
|
assignments.
|
||
|
|
||
|
<!-- -->
|
||
|
|
||
|
$ ( eval a=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Only works in Bash.
|
||
|
<a> <b c> <d>
|
||
|
$ ( x=a; eval "$x"=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Argument is no longer in the form of a valid assignment, therefore ordinary parsing rules apply.
|
||
|
-bash: syntax error near unexpected token `('
|
||
|
$ ( x=a; eval "$x"'=( a b\ c d )'; printf '<%s> ' "${a[@]}"; echo ) # Proper quoting then gives us the expected results.
|
||
|
<a> <b c> <d>
|
||
|
|
||
|
We don't know why Bash does this. Since parentheses are metacharacters,
|
||
|
they must ordinary be quoted or escaped when used as arguments. The
|
||
|
first example above is the same error as the second in all non-Bash
|
||
|
shells, even those with compound assignment.
|
||
|
|
||
|
In the case of `eval` it isn't recommended to use this behavior, because
|
||
|
unlike e.g. [declare](commands/builtin/declare), the initial expansion
|
||
|
is still subject to all expansions including
|
||
|
[word-splitting](syntax/expansion/wordsplit) and [pathname
|
||
|
expansion](syntax/expansion/glob).
|
||
|
|
||
|
$ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo "${x[@]}" )
|
||
|
+ touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
||
|
+ eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
||
|
++ x+=(\[[123]\]=*)
|
||
|
++ x+=([3]=yo)
|
||
|
+ echo '[[123]]=*' yo
|
||
|
[[123]]=* yo
|
||
|
|
||
|
Other commands known to be affected by compound assignment arguments
|
||
|
include: [let](commands/builtin/let),
|
||
|
[declare](commands/builtin/declare),
|
||
|
[typeset](commands/builtin/typeset), [local](commands/builtin/local),
|
||
|
[export](commands/builtin/export), and
|
||
|
[readonly](commands/builtin/readonly). More oddities below show both
|
||
|
similarities and differences to commands like
|
||
|
[declare](commands/builtin/declare). The rules for `eval` appear
|
||
|
identical to those of [let](commands/builtin/let).
|
||
|
|
||
|
## See also
|
||
|
|
||
|
- [BashFAQ 48 - eval and security
|
||
|
issues](http://mywiki.wooledge.org/BashFAQ/048) -- **IMPORTANT**
|
||
|
- [Another eval
|
||
|
article](http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F)
|
||
|
- [Indirection via
|
||
|
eval](http://mywiki.wooledge.org/BashFAQ/006#Assigning_indirect.2BAC8-reference_variables)
|
||
|
- [More indirection via
|
||
|
eval](http://fvue.nl/wiki/Bash:_Passing_variables_by_reference)
|
||
|
- [Martin Väth's "push"](https://github.com/vaeth/push) -- `printf %q`
|
||
|
work-alike for POSIX.
|