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.