Shell Script – Passing and Setting Variables in a Heredoc

here-documentshell-script

I have a script that has to do many different things on many different remote machines. I thought that a heredoc would work for this, but I am not able to use a variable defined elsewhere in the script and one defined in the heredoc.

Here is some code:

#!/bin/sh

FOO="foo"
ssh some.remote.host << EOF
  BAR="bar"
  echo "FOO=$FOO"
  echo "BAR=$BAR"
EOF

This only prints the following:

FOO=

BAR=bar

If, however, I quote the EOF line like this:
ssh some.remote.host << "EOF"
then it only prints the following:

FOO=foo

BAR=

Any hints on how I can use both variables inside the heredoc?

Thanks.

Best Answer

In short, use:

  • unquoted heredoc keywords, e.g., EOF
  • regular dollar char for outer (i.e. local) variables, e.g., $FOO
  • escaped dollar char for inner (i.e. remote) variables, e.g. \$BAR

If you leave the heredoc keyword (i.e. EOF) unquoted then the heredoc body is processed locally, so that $FOO is expanded to foo and BAR is expanded to the empty string. Then your ssh command becomes:

BAR="bar"
echo "FOO=foo"
echo "BAR="

If you quote the heredoc keyword then variable expansion is suppressed, so that your ssh command becomes this instead:

BAR="bar"
echo "FOO=$FOO"
echo "BAR=$BAR"

Since FOO is probably not defined in the remote shell environment, the expression "FOO=$FOO" is evaluated as "FOO=''", i.e. FOO is set to the empty string.

If you want to use both variables then you'll need to leave the heredoc keyword unquoted, so that variable expansion takes place for the locally defined variable, and then escape (with a backslash) the variable that you want to be expanded remotely, i.e.:

#!/bin/sh

FOO="foo"
ssh some.remote.host << EOF
  BAR="bar"
  echo "FOO=$FOO"
  echo "BAR=\$BAR"
EOF

In this case your ssh command (as received by the remote server) will be the following:

  BAR="bar"
  echo "FOO=foo"
  echo "BAR=$BAR"
Related Question