Great Escape for HereDoc

Recently at work I was putting together a bash script that created RPM packages. One of the scripts requirements was that .spec files had to be created at run time and their body should be enclosed in a HEREDOC block.

A HEREDOC block allows you to redirect a long, multi-line string without having to insert escape sequences like “\n” at the end of each line. A heredoc block starts with << followed by a delimiter that will be repeated on it’s own line to end the block. For example if I wanted to write a short paragraph to a file I could do something like this

cat <<EOF 
This is a short paragraph. That
I want to write to a file. It's 
not a big paragraph, but will 
do for this example.
EOF
> paragraph.txt     

In the example above EOF is the delimiter and all the text between <<EOF and EOF will be written to paragraph.txt. To increase readability, many add the ‘-‘ modifier to the opening heredoc delimiter to suppress leading tabs. This allows me to indent the contents with my heredoc without the indentation being written to the file.

cat <<-EOF 
    This is a short paragraph. That
    I want to write to a file. It's 
    not a big paragraph, but will 
    do for this example.

    Adding the tabs to the text makes
    it more readable, and easier to 
    spot where my end delimiter is.
EOF
> paragraph.txt     

The problem I ran into with using the heredoc for my RPM spec file was that my spec file contained bash in it’s %post section. When I ran the script containing the heredoc the bash within the heredoc also ran creating errors! There was a lot of variables and commands within the spec so escaping each “$” or “`” by end was not only tedious but provided fertile ground for some future maintainer to encounter the same problem. In desperation I typed “heredoc escape” into Google and discovered that simply wrapping the opening delimiter in quotes (single or double) will escape the entire contents of the string.

cat <<EOF 
To print out your current working directory enter `pwd`
EOF

Results in the following with the command replaced with it’s value.

To print out your current working directory enter /home/joatis

Wrapping quotes around the delimiter though preserves my intent:

cat <<-'EOF' 
    To print out your current working directory enter `pwd`
EOF

To print out your current working directory enter `pwd`

Adding the quotes keeps your content free of ugly escapes, improves readability and should prevent future maintenance mistakes.