Reputation: 6759
I have some code like this:
CMD=$(cat <<EOC
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
EOC
)
is there a way to get a here doc to assign result directly to variable in bash without intervening process (cat)? This code works, it just feels like too much work.
Upvotes: 11
Views: 7023
Reputation: 46813
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
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.
Upvotes: 28