Bash Assign Output to Variable Inside Here Document

How to assign a heredoc value to a variable in Bash?

You can avoid a useless use of cat and handle mismatched quotes better with this:

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

If you don't quote the variable when you echo it, newlines are lost. Quoting it preserves them:

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

If you want to use indentation for readability in the source code, use a dash after the less-thans. The indentation must be done using only tabs (no spaces).

$ read -r -d '' VAR <<-'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

If, instead, you want to preserve the tabs in the contents of the resulting variable, you need to remove tab from IFS. The terminal marker for the here doc (EOF) must not be indented.

$ IFS='' read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Tabs can be inserted at the command line by pressing Ctrl-V Tab. If you are using an editor, depending on which one, that may also work or you may have to turn off the feature that automatically converts tabs to spaces.

Using variables inside a bash heredoc

In answer to your first question, there's no parameter substitution because you've put the delimiter in quotes - the bash manual says:

The format of here-documents is:

      <<[-]word
here-document
delimiter

No parameter expansion, command substitution, arithmetic expansion, or
pathname expansion is performed on word. If any characters in word are
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. [...]

If you change your first example to use <<EOF instead of << "EOF" you'll find that it works.

In your second example, the shell invokes sudo only with the parameter cat, and the redirection applies to the output of sudo cat as the original user. It'll work if you try:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT

Can a bash heredoc put its result directly into a variable?

How to put a here-string into a variable in Bash:

In Bash use read with the -d delimiter set to null:

IFS= read -r -d '' cmd <<EOC
...blah blah...
EOC

Make sure you really use IFS= like shown, in front of read, otherwise any leading and trailing spaces will be trimmed. Make sure you use -r otherwise some backslashes would be understood as escape backslashes.

Some would argue that it's simpler to just use a plain assignment as:

cmd='
...blah blah...
'

But sometimes you have lots of quotes to the point that it becomes simpler and nicer to use this.

Subtle note. With this, read returns a failure return code (1) since the null-byte delimiter is not read before EOF. While this is alright most of the times, it can be a problem if you're using set -e (but you really shouldn't use set -e anyway). If you want to be sure, add:

IFS= read -r -d '' cmd <<EOC || true
...blah blah...
EOC

Now, seriously, about your problem.

Below is a serious note that you really should take into account: don't put code into strings! it's broken!. Instead, use a function or (still bad, but not broken) an array. Here's how you would use an array:

mycommand=(
docker run
-p "$MY_IP:$LOCAL_PORT:$LOCAL_PORT"
-p "$MY_IP:$PEER_PORT:$PEER_PORT"
-v "$CERT_DIR":/cert
"$ETCD_IMAGE"
--name "$MACHINE.$DOMAIN"
--peer-cert-file=/cert/server-cert.pem
--peer-key-file=/cert/server-key.pem
--peer-ca-file=/cert/ca.pem
--peer-addr="$MY_IP:$PEER_PORT"
--peers="$OIPPC"
)

(observe the quotes that I took time to type, with love).
Then you can safely run it (by safely I mean it's all right if you're having glob characters or quotes or spaces in your arguments) as:

"${mycommand[@]}"

(observe the healthy quotes, again). If you want to print the command, use this:

printf '%s\n' "${mycommand[*]}"

Unfortunately, the line breaks won't be preserved here. But really, that shouldn't be a problem at all. If really needed, you should pass this command through a formatter of some sort (well, very likely it doesn't exist so you'll have to code it yourself). But put the things in the right order: you want to define a command, to execute it (and, optionally format it, for user display), not the other way round, have a string that's nice to the user's eyes that you then have to parse (dangerously) to transform into code.

How to assign variables with Command results with HEREDOC in shell script

It's not at all clear to me what exactly what you are doing, but you should realize that the line a=$(cat $file) (I've used $() notation rather than backticks to avoid a clash with SO markup and because backticks are a scourge on humanity that should be avoided at all costs) is expanding both $file and the process substitution on the local host. Similarly, the ls -l is listing the directory on the local host from which you run Jump.Sh. You can avoid that by simply quoting the EOF:

Jump.Sh ... > sample.csv << 'EOF'

will prevent any expansion locally and pass the raw text as input to Jump.Sh

How do I set a variable to the output of a command in Bash?

In addition to backticks `command`, command substitution can be done with $(command) or "$(command)", which I find easier to read, and allows for nesting.

OUTPUT=$(ls -1)
echo "${OUTPUT}"

MULTILINE=$(ls \
-1)
echo "${MULTILINE}"

Quoting (") does matter to preserve multi-line variable values; it is optional on the right-hand side of an assignment, as word splitting is not performed, so OUTPUT=$(ls -1) would work fine.

How to pipe a here-document through a command and capture the result into a variable?

This seems to work (based on Ignacio's answer). By using a subshell the here-document is correctly piped into xsltproc while still being passed through tail after.

VERSION=$((xsltproc - ../pom.xml | tail -1) << EOF
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/"><xsl:value-of select="/project/version"/></xsl:template>
</xsl:stylesheet>
EOF
)

How to assign the output of a Bash command to a variable?

Try:

pwd=`pwd`

or

pwd=$(pwd)

Notice no spaces after the equals sign.

Also as Mr. Weiss points out; you don't assign to $pwd, you assign to pwd.



Related Topics



Leave a reply



Submit