Steve August 3, 2020
linux-fu:-help-messages-for-shell-scripts-and-here-documents

Imagine that you want to output multiple lines of text in Bash, or any shell script. Maybe it’s for a help string for a particularly convoluted shell script you’re writing. You could have a separate echo command for each line.  Or you could use the “here document“.

The “here document” construction takes the text between two delimiters and passes it, as if it were piped, to a command.

if [[$# == 0 ]] || [[ "$1" == "-h" ]]; then
cat << EOF
This is my help message. There are many like it but this one is mine.
My help message is my best friend.
EOF

All of the text, as written, with line breaks and spaces and all, get passed to cat and your helpful formatted message is printed to the user.

You can use here documents interactively at the command prompt, and you can use them in functions:

#!/bin/bash
help() {
    cat << EOF
Here is your help.
EOF
}

More About Here Documents

More than just writing help scripts, here documents are useful any time that you want to enter multiple lines and have them piped to a command. We use them most often from the shell directly. There are a few nuances, however.

You probably figured out that the “EOF” string in the above examples marks the beginning and end of the here document. There’s nothing special about it. You can use any word that doesn’t appear in your document. So “<< END_OF_HERE_DOC” would work.

The end of document string has to be at the beginning of a line, and by itself. You should not have whitespace before or after it. It is usually a good idea to use something odd and more than a few characters as the end string, although — in theory — you could use even a single character.

But that requirement messes up code indentation — see the help() function above.  If you put a dash between the redirection brackets and the string, the shell will consume any leading tab characters (but not spaces!). That lets you indent your message so it doesn’t all run together. But here, you have to be careful about spaces in the declaration: cat <<- EOF and cat <<-EOF work, while cat << -EOF doesn’t.  (It expects the delimiter “-EOF”.)  Still, it lets you indent your code if you can get it right, and use tabs instead of spaces.

 
#!/bin/bash 
help() { 
    cat <<- EOF 
        Here is your help. 
    EOF 
} 

Substitution

One thing that might not be obvious is that the here document can handle variable expansion. Try typing:

cat << EOF
Your path is $PATH
Your prompt is $PS1
EOF

Your path is /usr/local/sbin:/usr/local/bin:...
Your prompt is ($HOSTNAME w)$

That even works with things like command substitution. So you could put things in like $(ls), for example. Sometimes you don’t want that, though. In those cases, just quote the end string. For example:

cat << "EOF"
Your path is $PATH
Your prompt is $PS1
EOF

Your path is $PATH
Your prompt is $PS1

Peculiarities

You can even send the input to a program that does nothing like : which might seem odd. But some people use that to comment out large blocks of script for debugging or other purposes. It is a bit wasteful, because the lines you don’t want get put in a temporary file and fed to the input of a program that will ignore them, but you might see it done.

Clearly, here documents are niche. I use them a lot for writing help strings in scripts, but they’re just the ticket when you need to pass multiple lines in a script or to a script as you typed multiple lines into stdin.

Read More