mirror of
https://github.com/flokoe/bash-hackers-wiki.git
synced 2024-11-25 15:53:41 +01:00
73 lines
51 KiB
HTML
73 lines
51 KiB
HTML
|
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><link href=https://flokoe.github.io/bash-hackers-wiki/commands/builtin/eval/ rel=canonical><link href=../echo/ rel=prev><link href=../exec/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.5.44"><title>The eval builtin command - The Bash Hackers Wiki</title><link rel=stylesheet href=../../../assets/stylesheets/main.0253249f.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.06af60db.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=indigo data-md-color-accent=indigo> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#the-eval-builtin-command class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="The Bash Hackers Wiki" class="md-header__button md-logo" aria-label="The Bash Hackers Wiki" data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> The Bash Hackers Wiki </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> The eval builtin command </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=indigo data-md-color-accent=indigo aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=indigo data-md-color-accent=indigo aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=m
|
||
|
</code></pre></div> <h2 id=description>Description<a class=headerlink href=#description title="Permanent link">¶</a></h2> <p><code>eval</code> takes its arguments, concatenates them separated by spaces, and executes the resulting string as Bash code in the current execution environment. <code>eval</code> in Bash works in essentially the same way as most other languages that have an <code>eval</code> function. Perhaps the easiest way to think about <code>eval</code> is that it works in the same way as running \'\'bash -c \"bash code...\" \'\'from a script, except in the case of <code>eval</code>, the given code is executed in the current shell environment rather than a child process.</p> <h2 id=examples>Examples<a class=headerlink href=#examples title="Permanent link">¶</a></h2> <p>In this example, the literal text within the <a href=../../../syntax/redirection/#here_documents>here-document</a> is executed as Bash code exactly as though it were to appear within the script in place of the <code>eval</code> command below it.</p> <div class=highlight><pre><span></span><code>#!/usr/bin/env bash
|
||
|
{ myCode=$(</dev/stdin); } <<\EOF
|
||
|
... arbitrary bash code here ...
|
||
|
EOF
|
||
|
|
||
|
eval "$myCode"
|
||
|
</code></pre></div> <h3 id=expansion-side-effects>Expansion side-effects<a class=headerlink href=#expansion-side-effects title="Permanent link">¶</a></h3> <p>Frequently, <code>eval</code> 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 <code>eval</code> the most powerful command in all of shell programming (and in most other languages for that matter).</p> <p>This code defines a set of identical functions using the supplied names. <code>eval</code> is the only way to achieve this effect.</p> <div class=highlight><pre><span></span><code>main() {
|
||
|
local fun='() { echo "$FUNCNAME"; }' x
|
||
|
|
||
|
for x in {f..n}; do
|
||
|
eval "${x}${fun}"
|
||
|
done
|
||
|
|
||
|
"$@"
|
||
|
}
|
||
|
|
||
|
main "$@"
|
||
|
</code></pre></div> <h3 id=using-printf-q>Using printf %q<a class=headerlink href=#using-printf-q title="Permanent link">¶</a></h3> <p>The <code>printf %q</code> format string performs shell escaping on its arguments. This makes <code>printf %q</code> the "anti-eval" - with each pass of a string through printf requiring another <code>eval</code> to peel off the escaping again.</p> <div class=highlight><pre><span></span><code>while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do
|
||
|
printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}"
|
||
|
done
|
||
|
$evalBall
|
||
|
</code></pre></div> <p>The above example is mostly fun and games but illustrates the <code>printf %q</code> property.</p> <h3 id=higher-order-functions>Higher-order functions<a class=headerlink href=#higher-order-functions title="Permanent link">¶</a></h3> <p>Since all current POSIX-compatible shells lack support for <a href=http://en.wikipedia.org/wiki/First-class_function>first-class functions</a>, it can be tempting and sometimes useful to simulate some of their effect using <code>eval</code> to evaluate a string containing code.</p> <p>This example shows <a href=http://en.wikipedia.org/wiki/Partial_application>partial application</a> using <code>eval</code>.</p> <div class=highlight><pre><span></span><code>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
|
||
|
</code></pre></div> <p>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.</p> <h2 id=portability-considerations>Portability considerations<a class=headerlink href=#portability-considerations title="Permanent link">¶</a></h2> <ul> <li> <p>Unfortunately, because eval is a <strong>special builtin</strong>, 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 <code>command</code> regular builtin, but current versions of ~~ksh93~~ and zsh don't do this properly (<a href=http://article.gmane.org/gmane.comp.programming.tools.ast.devel/686>fixed</a> in ksh 93v- 2012-10-24 alpha). Earlier versions of zsh work (with <code>setopt POSIX_BUILTINS</code> -- looks like a regression). This works correctly in Bash POSIX mode, Dash, and mksh.</p> </li> <li> <p><code>eval</code> is another one of the few Bash builtins with keyword-like conditional parsing of arguments that are in the form of compound assignments.</p> <p>$ ( 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></p> </li> </ul> <p>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.</p> <p>In the case of <code>eval</code> it isn't recommended to use this behavior, because unlike e.g. <a href=../declare/ >declare</a>, the initial expansion is still subject to all expansions including <a href=../../../syntax/expansion/wordsplit/ >word-splitting</a> and <a href=../../../syntax/expansion/globs/ >pathname expansion</a>.</p> <div class=highlight><pre><span></span><code> $ ( 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
|
||
|
</code></pre></div> <p>Other commands known to be affected by compound assignment arguments include: <a href=../let/ >let</a>, <a href=../declare/ >declare</a>, <code>typeset</code>, <a href=../local/ >local</a>, <a href=../export/ >export</a>, and <a href=../readonly/ >readonly</a>. More oddities below show both similarities and differences to commands like <a href=../declare/ >declare</a>. The rules for <code>eval</code> appear identical to those of <a href=../let/ >let</a>.</p> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">¶</a></h2> <ul> <li><a href=http://mywiki.wooledge.org/BashFAQ/048>BashFAQ 48 - eval and security issues</a> -- <strong>IMPORTANT</strong></li> <li><a href=http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F>Another eval article</a></li> <li><a href=http://mywiki.wooledge.org/BashFAQ/006#Assigning_indirect.2BAC8-reference_variables>Indirection via eval</a></li> <li><a href=http://fvue.nl/wiki/Bash:_Passing_variables_by_reference>More indirection via eval</a></li> <li><a href=https://github.com/vaeth/push>Martin Väth's "push"</a> -- <code>printf %q</code> work-alike for POSIX.</li> <li><a href=http://www.chiark.greenend.org.uk/~sgtatham/aliases.html>The "magic alias" hack</a></li> </ul> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">November 7, 2024</span> </span> <span class=md-source-file__fact> <span class=md-icon title=Created> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M14.47 15.08 11 13V7h1.5v5.25l3.08 1.83c-.41.28-.79.62-1.11 1m-1.39 4.84c-.36.05-.71.08-1.08.08-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8c0 .37-.03.72-.08 1.08.69.1 1.33.32 1.92.64.1-.56.16-1.13.16-1.72 0-5.5-4.5-10-10-10S2 6.5 2 12s4.47 10 10 10c.59 0 1.16-.06 1.72-.16-.32-.59-.54-1.23-.64-1.92M18 15v3h-3v2h3v3h2v-3h3v-2h-3v-3z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">November 7, 2024</span> </span> </aside> <h2 id=__comments>Comments</h2> <script src=https://giscus.app/client.js data-repo=flokoe/bash-hackers-wiki data-repo-id=R_kgDOJ3Nr6Q data-category="Giscus Page Comments" data-category-id=DIC_kwDOJ3Nr6c4CXq9t data-mapping=pathname data-strict=1 data-reactions-enabled=1 data-emit-metadata=0 data-input-position=top data-theme=preferred_color_scheme data-lang=en data-loading=lazy crossorigin=anonymous async>
|
||
|
</script> <script>
|
||
|
var giscus = document.querySelector("script[src*=giscus]")
|
||
|
|
||
|
/* Set palette on initial load */
|
||
|
var palette = __md_get("__palette")
|
||
|
if (palette && typeof palette.color === "object") {
|
||
|
var theme = palette.color.scheme === "slate" ? "dark" : "light"
|
||
|
giscus.setAttribute("data-theme", theme)
|
||
|
}
|
||
|
|
||
|
/* Register event handlers after documented loaded */
|
||
|
document.addEventListener("DOMContentLoaded", function() {
|
||
|
var ref = document.querySelector("[data-md-component=palette]")
|
||
|
ref.addEventListener("change", function() {
|
||
|
var palette = __md_get("__palette")
|
||
|
if (palette && typeof palette.color === "object") {
|
||
|
var theme = palette.color.scheme === "slate" ? "dark" : "light"
|
||
|
|
||
|
/* Instruct Giscus to change theme */
|
||
|
var frame = document.querySelector(".giscus-frame")
|
||
|
frame.contentWindow.postMessage(
|
||
|
{ giscus: { setConfig: { theme } } },
|
||
|
"https://giscus.app"
|
||
|
)
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
</script> </article> </div> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"base": "../../..", "features": ["navigation.instant", "navigation.tracking", "navigation.tabs", "navigation.sections", "navigation.top", "content.action.view", "content.action.edit", "search.suggest", "search.highlight", "content.code.copy"], "search": "../../../assets/javascripts/workers/search.6ce7567c.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script> <script src=../../../assets/javascripts/bundle.83f73b43.min.js></script> </body> </html>
|