Shell by Example: Here Documents POSIX + Bash

Here-documents (heredocs) let you include multi-line text directly in your script. They’re great for embedding configuration files, SQL queries, or help text.

Basic syntax: <<DELIMITER ... DELIMITER

The delimiter can be any word (EOF is common). Variables like $HOME are expanded.

Edit
#!/bin/sh
cat <<EOF
This is a here-document.
It can span multiple lines.
Variables like $HOME are expanded.
EOF
Output:
This is a here-document.
It can span multiple lines.
Variables like /root are expanded.

Use << 'EOF' (quoted) to prevent variable expansion.

Edit
#!/bin/sh
cat <<'EOF'
This text is literal.
$HOME is not expanded here.
Special characters like \n are also literal.
EOF
Output:
This text is literal.
$HOME is not expanded here.
Special characters like \n are also literal.

Use <<-EOF to strip leading tabs (not spaces). This helps with indentation in scripts.

Edit
#!/bin/sh
cat <<-EOF
	This line had a leading tab.
	So did this one.
	The tabs are stripped from output.
	EOF
Output:
This line had a leading tab.
So did this one.
The tabs are stripped from output.

Here-documents are commonly used with commands that read from stdin. Send mail (example - won’t actually send):

Edit
#!/bin/sh
send_mail() {
    # readin in stdin
    message=$(cat)
    echo "message: $message"
}

send_mail -s "Report" user@example.com <<EOF
Daily report attached.
Generated on $(date).
EOF
Output:
message: Daily report attached.
Generated on Wed Jan  1 10:00:00 UTC 2025.

Create a configuration file file with a here-document by adding a redirection operator >/path/to/file to the beginning of the here-document.

Edit
#!/bin/sh
cat <<EOF >/tmp/config.example
# Configuration file
# Generated: $(date)

setting1=value1
setting2=value2
debug=false
EOF

echo "Created config file:"
cat /tmp/config.example
Output:
Created config file:
# Configuration file
# Generated: Wed Jan  1 10:00:00 UTC 2025

setting1=value1
setting2=value2
debug=false

Bash

Here-strings (<<<) provide a shorthand for passing a string directly to a command’s stdin:

Edit
#!/bin/bash
grep "in" <<<"search in this string"
Output:
search in this string

Bash

Here-strings are particularly useful with read:

Edit
#!/bin/bash
read -r first rest <<<"hello world from bash"
echo "First word: $first" # hello
Output:
First word: hello

Bash

They can include variable expansion:

Edit
#!/bin/bash
message="Hello, World!"
tr '[:lower:]' '[:upper:]' <<<"$message"
Output:
HELLO, WORLD!

Here-documents work great for multi-line variables.

Edit
#!/bin/sh
help_text=$(
    cat <<'EOF'
Usage: myscript [options] <file>

Options:
  -h, --help     Show this help message
  -v, --verbose  Enable verbose output
  -o FILE        Output to FILE

Examples:
  myscript input.txt
  myscript -v -o output.txt input.txt
EOF
)

echo "$help_text"
Output:
Usage: myscript [options] <file>

Options:
  -h, --help     Show this help message
  -v, --verbose  Enable verbose output
  -o FILE        Output to FILE

Examples:
  myscript input.txt
  myscript -v -o output.txt input.txt

« Arrays | Exit Codes »