Heredoc Basics - Embedding Multiline Strings in Shell Scripts

Heredoc Basics - Embedding Multiline Strings in Shell Scripts

What Is a Heredoc?

A heredoc (here document) lets you embed multiline strings directly in a shell script. It starts with <<DELIMITER and ends when the delimiter appears alone on a line. Compared to chaining multiple echo calls, heredocs are far more readable and are widely used for generating config files, running SQL queries, and feeding commands over SSH.

Basic Syntax

Write <<DELIMITER, then the body text, then the closing delimiter on its own line.

cat <<EOF
First line of text
Second line of text
Third line of text
EOF
First line of text
Second line of text
Third line of text

The delimiter can be any string — EOF, END, HEREDOC — but EOF (End Of File) is the conventional choice.

The closing delimiter must start at column zero. Any leading spaces or tabs will prevent it from being recognized, causing the script to hang waiting for input.

Variable Expansion Is On by Default

With <<EOF (unquoted), variables and command substitutions inside the heredoc are expanded by the shell before output.

NAME="Alice"
TODAY=$(date +%Y-%m-%d)
cat <<EOF
Hello, $NAME
Today is $TODAY
EOF
Hello, Alice
Today is 2026-06-01

To disable expansion, quote the delimiter with single quotes.

NAME="Alice"
cat <<'EOF'
Hello, $NAME
$(date) is not expanded
EOF
Hello, $NAME
$(date) is not expanded

When to use which

  • Embed shell variables in the output → <<EOF (default)
  • Output a template or code snippet verbatim → <<'EOF'

Strip Leading Tabs with <<-

A standard heredoc preserves all leading whitespace. With <<-, only leading tab characters are stripped. This lets you indent the heredoc body to match the surrounding code without the indentation appearing in the output.

if true; then
	cat <<-EOF
	Indented block
	Tabs are stripped from output
	EOF
fi
Indented block
Tabs are stripped from output

Only tab characters (\t) are stripped — not spaces. If your editor uses space indentation, you need to convert those lines to tabs for <<- to work.

Writing to Files

Combine a heredoc with redirection to generate config files directly from a script.

cat <<EOF > /tmp/config.conf
host=localhost
port=5432
dbname=mydb
EOF

To append instead of overwrite, use >>:

cat <<EOF >> /tmp/config.conf
user=admin
password=secret
EOF

For files that need root permissions, pipe through sudo tee. Using sudo cat <<EOF > /root/file does not work because the shell opens the redirect target before sudo takes effect.

cat <<EOF | sudo tee /etc/myapp/config.conf
host=localhost
port=5432
EOF

Practical Example — Running Commands Over SSH

ssh user@server <<EOF
echo "Starting deployment"
cd /var/www/myapp
git pull origin main
systemctl restart myapp
EOF

Use <<EOF when you want variables expanded locally before the string is sent to the remote shell. Use <<'EOF' when you want the remote shell to expand them instead.

Practical Example — Feeding Queries to MySQL

mysql -u root -p mydb <<EOF
SELECT id, name FROM users WHERE active = 1;
UPDATE users SET last_login = NOW() WHERE id = 42;
EOF

This pattern is common in initialization scripts and batch jobs that need to run multiple SQL statements in a single session.

Common Pitfalls

No spaces or tabs before the closing delimiter

# Wrong: the closing EOF has leading spaces and is never recognized
cat <<EOF
content
  EOF
EOF

The closing delimiter must begin at column zero (or use <<- with tabs only).

Escaping dollar signs for literal output

With <<EOF (expansion enabled), prefix $ with a backslash to output it literally.

cat <<EOF
Home directory variable: \$HOME
Actual value: $HOME
EOF
Home directory variable: $HOME
Actual value: /home/alice

Capturing a heredoc into a variable

To store a heredoc in a variable rather than piping it to a command, use command substitution with $().

TEXT=$(cat <<EOF
multiline
text stored
in a variable
EOF
)
echo "$TEXT"

Next Reading