eval: Tells the shell what to do

Edit: Take a look at Michael’s explanation below. He seems to do a better job explaining eval than I did. 😐

I know this is an internal command, and I should probably wave it off as part of bash, but this one I use quite a lot. So I feel obligated to mention it.

eval interprets and expands a command before the shell can get its hands on it. That sounds cryptic, and in a way, it is.

But what that means is, eval will pass a command it finds elsewhere to the shell.

So let’s say, for example, you have a text file that tells feh what background image to use.

feh  --bg-scale '/home/kmandla/wallpaper/1919x1199-malaria.jpg'

I like to give my wallpaper wacky names. Yes, that is the contents of the file. If you have a .fehbg file on your computer, it might look something like that in there.

How do we pump that into bash, to get feh started and doing its wallpaper thing? eval, of course.

eval `cat .fehbg`

And what does it do? eval takes the results of cat .fehbg and passes it to the shell. So, the contents of .fehbg are dumped through to bash, and we get a malarial background on the desktop.

What’s another example? Remember back when slmenu was the newest thing on the block, and not just a common everyday piece of software? Well, if you recall, slmenu’s output was just the selected string … probably a name for a program, but maybe not. Maybe with some arguments.

And how did that program get sent through to the shell, and slmenu to finally do its job? With eval, of course.

4 thoughts on “eval: Tells the shell what to do

  1. Loomx

    I’ve been scared off using `eval’ by various things I’ve read [1], but in these cases you don’t even need it:
    `cat .fehbg` on its own works (it’s the backticks that make the shell re-run the result of the first command)

    Same with the `slmenu’ command; it works just the same without the `eval’

    [1] e.g. http://mywiki.wooledge.org/BashFAQ/048

  2. Michael Hodgin

    I realize the guy above corrected you and you accepted, but can you change the post? It’s really, really wrong. Dangerously so. Eval does not “tell the shell what to do;” eval IS the shell! Eval is a required Posix builtin. What eval does is parse a command TWICE. The Posix guidelines describe it as concatenating space-separated arguments into a single shell command, but it’s really important to understand that it does this TWICE in the CURRENT SHELL ENVIRONMENT especially where quotes are concerned.

    For instance, as a very simple example, consider the simple stdin file-type argument grabber many shell scripts use:
    cat “$@” ## basically just pipes the contents of file arguments in single stream to your /dev/tty
    ## add eval
    eval cat “$@”

    First the shell strips quotes and applies the special rules to quoted “$@” to separate each argument with $IFS while retaining their original combinations as passed in quotes. This is important because in the FIRST pass a quoted argument is just a string like a variable or filename or what-have-you. And, for that matter, each argument is adjudged safe as applied to cat because it’s just concatenating the command-line arguments provided on , which are both just links to the same $(tty) file. Of course, on the second pass that changes because the unquoted cat utility successfully executed and needs no further execution, instead, as you note in your own article, eval will execute cat’s output sans one layer of shell-quotes.

    eval cat “$@” ## pass 1
    ## essentially becomes
    sh <<-EOF ## pass 2
    $@
    EOF

    Basically if any of those arguments is a file or a variable or a quoted string containing shell code that shell code will be executed in the current shell environment. Eval will attempt to execute as a shell command cat's output, including the contents of any files fed it as arguments.

    Eval should never be used to process data that isn't very strictly controlled and sanitized by its caller. Very simple things can totally monkey-wrench the whole deal. Here's another and I'll put away the soap box:

    eval cmd_var="cd /tmp ; rm *" #pass 1 … fine without eval
    cmd_var=cd /tmp ; rm * #pass 2 after quote removal … yikes

  3. Pingback: cheat, cheat, cheats and cheat: So many cheaters | Inconsolation

Comments are closed.