Introduction to Heredoc
Heredoc is a way to pass multi-line input to a command or language without fussing over escaping or newline characters. It's one of those quiet tools that saves you a surprising amount of time once you understand the mechanics.
I'll cover the basics of heredoc, the distinction between heredoc and nowdoc, where they shine in practice, and how they work in Bash and Python.
The basic idea
At its core, heredoc lets you embed a block of text directly in your script or command line. You choose a delimiter — any word you like — and everything until that word appears on a line by itself becomes the input.
cat << EOF
Hello, world!
This is line two.
EOF
The shell reads everything between the opening << EOF and the closing EOF as the standard input to cat. No need to quote, escape, or concatenate strings.
Heredoc vs nowdoc
Not all heredocs are the same. The key distinction is variable interpolation — whether placeholders inside the block get expanded or left as literal text.
Heredoc (Bash, PHP, Ruby) expands variables inside the block:
name="Gifted"
cat << EOF
Hello, $name
Today is $(date +%A)
EOF
Output:
Hello, Gifted
Today is Monday
Nowdoc (PHP, and conceptually in Python with raw strings) treats the block as literal — no variable expansion happens. In PHP, you quote the delimiter:
$name = "Gifted";
echo <<<'EOF'
Hello, $name
This stays exactly as typed.
EOF;
The distinction matters when you need to preserve dollar signs, backticks, or template literals as-is — configuration files, SQL strings, or code generation.
Common use cases
Heredocs excel anywhere you'd otherwise reach for echo chains or messy quoting:
- Generating config files — Write entire configs inline instead of editing them separately.
- Running SQL queries — Pass multi-line SQL to
mysql,psql, orsqlite3without escaping. - Here documents in tests — Embed expected output or fixture data directly in test files.
- Sending emails — Compose email bodies inline for
sendmailormail. - Embedding templates — Build HTML, Markdown, or other structured text from variables.
Python's take
Python doesn't have a built-in heredoc operator, but achieves the same result with triple-quoted strings:
import sqlite3
conn = sqlite3.connect("users.db")
cur = conn.cursor()
query = """
SELECT id, name, email
FROM users
WHERE active = 1
ORDER BY name;
"""
cur.execute(query)
Triple quotes (""" or ''') preserve newlines and indentation naturally. Use ''' (or r""") when you need a raw string — Python's equivalent of nowdoc — to avoid escaping backslashes or dollar signs.
You can also combine this with textwrap.dedent() to strip leading whitespace from indented blocks:
from textwrap import dedent
config = dedent("""
[database]
host = localhost
port = 5432
name = myapp
""").strip()
Bash deep dive
Bash heredoc has a couple of useful variants. The standard form:
cat << EOF
Some text here
EOF
Stripping leading tabs — Prefix the delimiter with - to remove leading tab characters (not spaces). This keeps your heredoc indented in a script without adding tabs to the output:
if true; then
cat <<- EOF
This has no leading tabs
EOF
fi
Quoting the delimiter — 'EOF' turns off variable expansion, giving you nowdoc-like behavior:
price='$100'
cat << 'EOF'
The price is $price
EOF
# Output: The price is $price
Without the quotes, $price would expand to 100.
When to reach for heredoc
Use heredoc when the text you're passing is long enough that inline quotes become unwieldy, or when the content itself contains quotes, newlines, or special characters that would be painful to escape.
If the text is short and simple, a quoted string is still cleaner. Heredoc is a tool, not a default — but when the situation calls for it, nothing else feels as natural.
I reach for it constantly in scripts that generate configs, seed databases, or compose structured text. Once you internalize the syntax, you'll spot the use cases everywhere.