5

GNU Bash - 3.6.6 Here Documents

[n]<<[-]word
        here-document
delimiter

If any part of word is quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion, the character sequence \newline is ignored, and ‘\’ must be used to quote the characters ‘\’, ‘$’, and ‘`’.

If I single-quote EOF, it works. I think because bash /bin/bash process to be invoked gets un-expanded strings and then the invoked process interprets the lines.

$ /bin/bash<<'EOF'
#!/bin/bash
echo $BASH_VERSION
EOF               
3.2.57(1)-release

However, the below is causing an error. I thought BASH_VERSION would have been expanded and the version of current bash process is passed to the /bin/bash process to be invoked. But not working.

$ /bin/bash<<EOF 
#!/bin/bash
echo $BASH_VERSION
EOF               
/bin/bash: line 2: syntax error near unexpected token `('
/bin/bash: line 2: `echo 5.0.17(1)-release'
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
mon
  • 18,789
  • 22
  • 112
  • 205

3 Answers3

5
/bin/bash<<EOF 
#!/bin/bash
echo $BASH_VERSION
EOF

As you can infer from the error message, the heredoc is being expanded to:

/bin/bash<<EOF 
#!/bin/bash
echo 5.0.17(1)-release
EOF

It sounds like that's what you expect: it's being expanded to the outer shell's version. The problem isn't with the heredoc or the expansion; it's that unquoted parentheses are a syntax error. Try running just the echo command by hand and you'll get the same error:

$ echo 5.0.17(1)-release
bash: syntax error near unexpected token `('

To fix this, you could add extra quotes:

/bin/bash<<EOF 
echo '$BASH_VERSION'
EOF

This will work and print the outer shell's version. I used single quotes to demonstrate that these quotes will not inhibit variable expansion. The outer shell doesn't see these quotes. Only the inner shell does.

(I also got rid of the #!/bin/bash shebang line. There's no need for it since you're explicitly invoking bash.)

However, quoting is not 100% robust. If $BASH_VERSION happened to contain single quotes you'd have a problem. The quotes make parentheses ( ) safe but they aren't foolproof. As a general technique, if you want this to be completely safe no matter what special characters are in play you'll have to jump through some ugly hoops.

  1. Use printf '%q' to escape all special characters.

    /bin/bash <<EOF
    echo $(printf '%q' "$BASH_VERSION")
    EOF
    

    This will expand to echo 5.0.17\(1\)-release.

  2. Pass it in as an environment variable and use <<'EOF' to disable interpolation inside the script.

    OUTER_VERSION="$BASH_VERSION" /bin/bash <<'EOF'
    echo "$OUTER_VERSION"
    EOF
    

    This would be my choice. I prefer use the <<'EOF' form whenever possible. Having the parent shell interpolate the script being passed to a child shell can be confusing and difficult to reason about. Also, the explicit $OUTER_VERSION variable makes it crystal clear what's happening.

  3. Use bash -c 'script' instead of a heredoc and then pass the version in as a command-line argument.

    bash -c 'echo "$1"' bash "$BASH_VERSION"
    

    I might go with this for a single-line script.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
2

If you don't quote EOF, variables in the heredoc are expanded by the original shell before passing it as input to the invoked shell. So it's equivalent to executing

echo 3.2.57(1)-release

in the invoked shell. That's not valid bash syntax, so you get an error.

Quoting the word prevents variable expansion, so the invoked shell receives $BASH_VERSION literally, and expands it itself.

Barmar
  • 741,623
  • 53
  • 500
  • 612
0

In the first case, the quotes prevent any changes in the here document, so the sub-shell sees echo $BASH_VERSION and it expands the string and echoes it.

In the second case, the absence of quotes means that the first shell expands the information and it sees echo 3.2.57(1)-release, and if you type that at the command line, you get the syntax error.

If you used echo "$BASH_VERSION" in both, then both would work, but different shells would expand $BASH_VERSION.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278