How to avoid heredoc expanding variables?

TheCodeKiller picture TheCodeKiller · Jan 13, 2015 · Viewed 32k times · Source

I'm trying to create a script file using substitution string from ENV but want also to prevent some from escaping

export PLACEHOLDER1="myPlaceholder1Value"
export PLACEHOLDER2="myPlaceholder2Value"
sudo /bin/su -c "cat << EOF > /etc/init.d/my-script
#!/bin/bash
#
### BEGIN INIT INFO
# Provides:          my-script
# Required-Start:    \$remote_fs \$syslog
# Required-Stop:     \$remote_fs \$syslog
# Should-Start:      \$network \$time
# Should-Stop:       \$network \$time
# Default-Start:     2 3 4 5 
# Default-Stop:      0 1 6
# Short-Description: blabla
# Description:       bla bla desc
#
### END INIT INFO
#
myvariable_1=toto$PLACEHOLDER1
myvariable_2=titi$PLACEHOLDER2
myvariable_final=\"dynamicvar=\${myvariable_1},\${myvariable_2}\"
EOF
"

It results in which is not good as the myvariable_final are not escaped and substituted as the one from the init script dependencies ($remote_fs, $syslog, $network, $time)

#!/bin/bash
#
### BEGIN INIT INFO
# Provides:          my-script
# Required-Start:
# Required-Stop:
# Should-Start:
# Should-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: blabla
# Description:       bla bla desc
#
### END INIT INFO
#
myvariable_1=totomyPlaceholder1Value
myvariable_2=titimyPlaceholder2Value
myvariable_final="dynamicvar=,"

If i try to put a backslash \ behind the dollars $, I manage to avoid the substitution but I getting an unwanted backslash \:

export PLACEHOLDER1="myPlaceholder1Value"
export PLACEHOLDER2="myPlaceholder2Value"
sudo /bin/su -c "cat << EOF > /etc/init.d/my-script
#!/bin/bash
#
### BEGIN INIT INFO
# Provides:          my-script
# Required-Start:    \$\remote_fs \$\syslog
# Required-Stop:     \$remote_fs \$syslog
# Should-Start:      \$network \$time
# Should-Stop:       \$network \$time
# Default-Start:     2 3 4 5 
# Default-Stop:      0 1 6
# Short-Description: blabla
# Description:       bla bla desc
#
### END INIT INFO
#
myvariable_1=toto$PLACEHOLDER1
myvariable_2=titi$PLACEHOLDER2
myvariable_final=\"dynamicvar=\$\{myvariable_1},\$\{myvariable_2}\"
EOF
"

results in:

#!/bin/bash
#
### BEGIN INIT INFO
# Provides:          my-script
# Required-Start:    $\remote_fs $\syslog
# Required-Stop:
# Should-Start:
# Should-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: blabla
# Description:       bla bla desc
#
### END INIT INFO
#
myvariable_1=totomyPlaceholder1Value
myvariable_2=titimyPlaceholder2Value
myvariable_final="dynamicvar=$\{myvariable_1},$\{myvariable_2}"

Wanted/attended result whould have been :

#!/bin/bash
#
### BEGIN INIT INFO
# Provides:          my-script
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Should-Start:      $network $time
# Should-Stop:       $network $time
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: blabla
# Description:       bla bla desc
#
### END INIT INFO
#
myvariable_1=totomyPlaceholder1Value
myvariable_2=titimyPlaceholder2Value
myvariable_final="dynamicvar=${myvariable_1},${myvariable_2}"

solved by putting quote around the EOF as below and using backslash to control the escaping when needed

export PLACEHOLDER1="myPlaceholder1Value"
export PLACEHOLDER2="myPlaceholder2Value"
sudo /bin/su -c "cat << 'EOF' > /etc/init.d/my-script
#!/bin/bash
#
### BEGIN INIT INFO
# Provides:          my-script
# Required-Start:    \$remote_fs \$syslog
# Required-Stop:     \$remote_fs \$syslog
# Should-Start:      \$network \$time
# Should-Stop:       \$network \$time
# Default-Start:     2 3 4 5 
# Default-Stop:      0 1 6
# Short-Description: blabla
# Description:       bla bla desc
#
### END INIT INFO
#
myvariable_1=toto$PLACEHOLDER1
myvariable_2=titi$PLACEHOLDER2
myvariable_final=\"dynamicvar=\${myvariable_1},\${myvariable_2}\"
EOF
"

Answer

fedorqui &#39;SO stop harming&#39; picture fedorqui 'SO stop harming' · Jan 13, 2015

Just use 'EOF' to prevent the variable from expanding:

sudo /bin/su -c "cat << 'EOF' > /etc/init.d/my-script
#                       ^   ^

From man bash:

Here Documents

This type of redirection instructs the shell to read input from the current source until a line containing only delimiter (with no trailing blanks) is seen. All of the lines read up to that point are then used as the standard input for a command.

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. In the latter case, the character sequence \ is ignored, and \ must be used to quote the characters \, $, and `.