Search code examples
bashshelleval

How to separate multiple commands passed to eval in bash


I'm trying to evaluate multiple lines of shell commands using eval, but when I try to resolve variables with eval separated by a newline \n the variables are not resolved.

x='echo a'
y='echo b'
z="$x\n$y"
eval $x
eval $y
eval $z

Which outputs:

a
b
anecho b

The last command gives anecho b, and apparently \n was treated as n there. So is there a way to evaluate multiple lines of commands (say, separated by \n)?


Solution

  • \n is not a newline; it's an escape sequence that in some situations will be translated into a newline, but you haven't used it in one of those situations. The variable $z doesn't wind up containing a newline, just backslash followed by "n". As a result, this is what's actually being executed:

    $ echo a\necho b
    anecho b
    

    You can either use a semicolon instead (which requires no translation), or use \n in a context where it will be translated into a newline:

    $ newline=$'\n'
    $ x='echo a'
    $ y='echo b'
    $ z="$x$newline$y"
    $ eval "$z"
    a
    b
    

    Note the double-quotes around "$z" -- they're actually critical here. Without them, bash will word-split the value of $z, turning all whitespace (spaces, tabs, newlines) into word breaks. If that happens, eval will receive the words "echo" "a" "echo" b", effectively turning the newline into a space:

    $ eval $z
    a echo b
    

    This is yet another in the long list of cases where it's important to double-quote variable references.