Heredocs und Herestrings sind Bashs Antwort auf eine simple Frage: Wie schiebe ich einem Befehl mehrzeiligen oder kurzen Inline-Text als stdin unter, ohne eine Hilfsdatei anzulegen? Von generierten Konfigfiles über SQL-Queries bis zum kurzen read-Idiom — wer Heredocs souverän einsetzt, schreibt deutlich kompaktere und lesbarere Skripte. Dieser Artikel zeigt alle Varianten, die häufigsten Patterns und die Stolperfallen, die selbst geübte Skripter regelmäßig erwischen.

Was Heredocs sind

Ein Heredoc (kurz für „here document”) ist eine spezielle Form der Eingabe-Umleitung: Statt aus einer Datei oder einer Pipe liest der Befehl seinen stdin aus dem Skript selbst — direkt aus den Zeilen zwischen einem öffnenden Marker und einem identisch geschriebenen End-Marker. Die Shell bündelt diesen Block intern, schreibt ihn in eine temporäre Pipe oder Datei und übergibt ihn dem Prozess als Standardeingabe.

Bash Heredoc als stdin
cat <<EOF
Erste Zeile
Zweite Zeile
EOF
Output
Erste Zeile
Zweite Zeile

Der Marker EOF ist reine Konvention — du kannst auch END, SQL oder CONFIG schreiben. Wichtig ist nur, dass der Marker im Skript an genau einer Stelle als Anfang und an einer als Ende auftaucht, und dass der End-Marker alleine in einer eigenen Zeile steht.

Heredocs sind eine Bash-Erweiterung mit POSIX-Unterstützung — sie funktionieren in Bash, Zsh, Dash und sh. Anders als Process Substitution sind sie also portabel.

Grundsyntax <<EOF ... EOF

Die Standardform ist <<MARKER. Alles zwischen der Marker-Zeile und dem End-Marker wird als stdin an den vorangehenden Befehl übergeben. Variablen, Command-Substitutions und Backticks werden dabei wie in doppelten Anführungszeichen expandiert.

Bash Variablen-Expansion im Heredoc
name="Michael"
host="$(hostname)"
cat <<EOF
Hallo $name,
du arbeitest gerade auf $host.
Heute ist $(date +%A).
EOF
Output
Hallo Michael,
du arbeitest gerade auf macbook.
Heute ist Montag.

Die Marker-Zeile selbst gehört nicht zum Inhalt. Auch der End-Marker erscheint nicht in der Ausgabe — er ist nur das Signal an den Parser, dass der Block hier endet. Wer ein $ wörtlich braucht (ohne Variablen-Expansion), escapt es mit Backslash: \$variable bleibt als Literal stehen.

Mit Tabs einrücken: <<-EOF

In sauber strukturierten Skripten will man den Heredoc-Inhalt einrücken, damit er sich in den umgebenden Block einfügt. Die normale Form <<EOF macht das unmöglich, weil jede führende Whitespace-Zeile erhalten bleibt — der End-Marker müsste dann ebenfalls am linken Rand stehen, was die Optik bricht.

Die Variante <<-EOF löst das: Sie entfernt führende Tabs aus jeder Zeile des Heredocs und auch vor dem End-Marker. Damit kannst du Inhalt und Marker mit dem umgebenden Code mit-einrücken.

Bash Tabs werden entfernt
if true; then
	cat <<-EOF
		Erste Zeile
		Zweite Zeile
	EOF
fi
Output
Erste Zeile
Zweite Zeile

Entscheidend: Es werden ausschließlich Tabs entfernt — keine Leerzeichen. Wer mit Spaces einrückt (etwa weil der Editor automatisch Tabs in Spaces umwandelt), erhält den Whitespace ungewollt im Output, und der End-Marker wird gar nicht erkannt, weil er nicht am Zeilenanfang steht. Für <<- gilt also: Editor auf echte Tabs konfigurieren, oder gleich auf die normale Form <<EOF zurückgreifen.

Zitiert: <<'EOF' ohne Expansion

Soll der Heredoc-Inhalt wörtlich übergeben werden — ohne Variablen, ohne Command-Substitution, ohne Backticks — setzt man den Marker in einfache Anführungszeichen: <<'EOF'. Jede Variante mit Quotes funktioniert (<<"EOF", <<\EOF), die einfachen Anführungszeichen sind die übliche Form.

Bash Keine Expansion bei zitiertem Marker
cat <<'EOF'
Pfad: $HOME
Datum: $(date)
Backtick: `whoami`
EOF
Output
Pfad: $HOME
Datum: $(date)
Backtick: `whoami`

Diese Form ist Pflicht, sobald du Code als Daten ablegst — etwa ein Shell-Skript, das von einem anderen Skript erzeugt wird, ein AWK-Programm, eine Python-Snippet-Datei oder ein Template mit Platzhaltern, das später erst ausgefüllt wird. Ohne Quoting würde Bash jedes $var oder $(...) schon im Heredoc auswerten — und damit den Inhalt zerschießen, bevor er überhaupt gespeichert ist.

Mischformen gibt es nicht: Entweder die ganze Heredoc expandiert, oder gar nichts. Wer einzelne Stellen schützen will, lässt die normale Form und escapt punktuell mit Backslash: \$var bleibt literal, $other wird expandiert.

Herestrings <<<

Für einzelne Strings ist ein vollständiges Heredoc oft Overkill. Bash bietet dafür den Herestring mit drei spitzen Klammern: cmd <<< "wert". Der String wird mit angehängtem Newline auf die stdin von cmd gelegt — das war’s.

Bash Herestring an wc
wc -w <<< "drei kleine worte"
Output
3

Klassisch ist der Einsatz mit read: Statt einen String über Pipe an read zu schicken (was read in eine Subshell verbannt und die zugewiesenen Variablen verschluckt), schreibt man elegant:

Bash read aus Herestring
wert="alpha:beta:gamma"
IFS=":" read -r a b c <<< "$wert"
echo "a=$a, b=$b, c=$c"
Output
a=alpha, b=beta, c=gamma

Das funktioniert genau deshalb, weil Herestrings in derselben Shell laufen — keine Subshell, keine verlorenen Variablen. Auch grep auf einem String fühlt sich damit natürlicher an als mit echo "..." | grep:

Bash grep auf String
text="error: file not found"
grep -o "error" <<< "$text"
Output
error

Heredoc in Subshell, Pipe und Process Substitution

Heredocs sind nichts weiter als eine spezielle stdin-Quelle und lassen sich deshalb mit allen anderen Redirection-Mechanismen kombinieren. Klassisch ist die Pipe an einen weiteren Verarbeitungsschritt:

Bash Heredoc durch Pipe
cat <<EOF | grep "wichtig"
unwichtig 1
wichtig 2
unwichtig 3
EOF

Auch in einer Subshell oder Process Substitution funktioniert das wie erwartet:

Bash Heredoc und Process Substitution
diff <(cat <<'EOF'
alpha
beta
EOF
) <(cat <<'EOF'
alpha
gamma
EOF
)

In der Praxis greift man dann meist doch zu Herestrings oder Variablen — die ineinander verschachtelten Heredocs werden schnell unübersichtlich. Der saubere Weg ist oft, den Heredoc-Inhalt vorher in eine Variable zu lesen und diese weiterzuverwenden.

Praxis-Patterns

Heredocs glänzen, wenn ein Befehl Inline-Daten erwartet — sei es eine Konfiguration, eine Query oder ein längerer Text. Die folgenden Muster decken den Großteil der täglichen Anwendungsfälle ab.

Konfigfile generieren ist der klassische Use-Case: Ein Skript schreibt eine fertige .conf mit eingesetzten Werten. Dank Variablen-Expansion fügt der Heredoc die aktuellen Werte direkt ein, ohne dass du sed oder awk bemühen müsstest.

Bash nginx-Snippet generieren
domain="example.org"
port=8080

cat > /etc/nginx/sites-available/$domain.conf <<EOF
server {
    listen 80;
    server_name $domain;

    location / {
        proxy_pass http://localhost:$port;
        proxy_set_header Host \$host;
    }
}
EOF

Beachte das \$host: nginx erwartet dort seine eigene Variable, nicht eine vom Skript expandierte Shell-Variable — deshalb der Backslash. Wer das auf vielen Stellen braucht, fährt mit <<'EOF' (zitiertes Heredoc) und einer expliziten Template-Substitution besser.

SQL-Query an psql oder mysql funktioniert nach demselben Schema. Der Heredoc liefert die Query als stdin, expansionssicher mit zitiertem Marker oder mit Bash-Variablen für dynamische Filter.

Bash Query an Postgres
user_id=42

psql -d shop <<EOF
SELECT name, email
FROM users
WHERE id = $user_id
ORDER BY created_at DESC;
EOF

Multi-Line-Echo ohne printf-Akrobatik: cat <<EOF ist die lesbarste Variante, mehrere Zeilen am Stück auszugeben — gerade für Hilfetexte oder Banner.

Bash Hilfetext ausgeben
usage() {
    cat <<'EOF'
Verwendung: deploy.sh [OPTIONS] TARGET

Optionen:
  -h, --help     Diese Hilfe anzeigen
  -v, --verbose  Ausführliche Ausgabe
  -n, --dry-run  Nichts wirklich ausführen
EOF
}

Hier ist der zitierte Marker <<'EOF' Pflicht: Sonst würde Bash versuchen, $TARGET und Konsorten zu expandieren — und beim ersten unbekannten Namen mit set -u abstürzen.

Heredoc als Dokumentation im Skript ist ein selten genutzter, aber eleganter Trick: Mit : (dem Null-Befehl) verwirfst du den Inhalt sofort — kommentierst aber einen langen Block ohne # vor jeder Zeile.

Bash Block-Kommentar via Heredoc
: <<'COMMENT'
Dieser ganze Block ist nur Doku.
Hier könnten ausführliche Erklärungen
stehen, ohne dass jede Zeile mit # beginnt.
COMMENT

String in read mit Herestring ist das idiomatische Pattern, einen einzelnen String in mehrere Variablen zu zerlegen — ohne Subshell-Falle:

Bash String in Variablen zerlegen
line="2026-05-04 ERROR Datenbank nicht erreichbar"
IFS= read -r datum level rest <<< "$line"
echo "Datum: $datum"
echo "Level: $level"
echo "Rest:  $rest"
Output
Datum: 2026-05-04
Level: ERROR
Rest:  Datenbank nicht erreichbar

Das IFS= direkt vor read ändert das Field-Separator nur für diesen einen Aufruf — read selbst nutzt aber sein eigenes IFS-Verhalten und teilt am Default-Whitespace, weshalb die drei Variablen sauber gefüllt werden.

Häufige Stolperfallen

Spaces vor dem Marker bei `<<-` werden NICHT entfernt

Die Variante <<-EOF strippt nur Tabs vom Zeilenanfang — Leerzeichen bleiben unangetastet. Wer den End-Marker mit Spaces einrückt, erhält die kryptische Meldung „warning: here-document delimited by end-of-file (wanted EOF)” oder die Heredoc läuft schlicht bis zum Skript-Ende. Editor auf echte Tabs konfigurieren oder auf die normale Form <<EOF zurückgreifen, bei der Marker und Inhalt am linken Rand stehen.

End-Marker MUSS allein in eigener Zeile stehen

Der schließende Marker wird nur erkannt, wenn er alleine in einer Zeile steht — nichts davor (außer Tabs bei <<-), nichts danach, kein Semikolon, kein Kommentar. Wer aus Reflex EOF; schreibt oder einen Hinweis hinten dranhängt, bekommt einen offenen Heredoc, der bis zum Dateiende reicht und meist mit „unexpected end of file” knallt. Auch ein abschließendes Whitespace nach dem Marker kann je nach Bash-Version Probleme machen — sauber Enter drücken.

Heredoc in Pipe läuft in Subshell

Wenn der Heredoc-Befehl in eine Pipe läuft, etwa cat &lt;&lt;EOF ... | while read line; do ...; done, läuft die Empfänger-Schleife in einer Subshell. Variablen, die in der Schleife gesetzt werden, sind nach dem Schleifen-Ende verloren. Lösung: statt Pipe ein Process-Substitution-Pattern wählen — while read line; do ...; done &lt; &lt;(cat &lt;&lt;EOF ... EOF). Dann läuft die Schleife in der Haupt-Shell.

Unterschied `<<<` vs. Pipe — same shell vs. Subshell

cmd &lt;&lt;&lt; "$str" läuft in derselben Shell, echo "$str" | cmd startet cmd in einer Subshell. Für read ist das der entscheidende Unterschied: read x &lt;&lt;&lt; "$str" füllt x in der aktuellen Shell, echo "$str" | read x füllt x in einer Subshell, die sofort danach beendet wird — x ist außen leer. Wer Variablen aus read weiterverwendet, nimmt immer den Herestring oder eine Process Substitution.

Variablen-Expansion mit Backslash escapen

In nicht-zitierten Heredocs werden &#36;var, &#36;(...) und Backticks ausgewertet — was für nginx-Configs, awk-Snippets oder Python-Code im Heredoc tödlich ist. Punktuell hilft Backslash: \&#36;var bleibt literal, \\ wird zu \. Bei mehr als zwei, drei Stellen lohnt sich die zitierte Form &lt;&lt;'EOF', die alles wörtlich nimmt — und dann mit sed/envsubst gezielt nach-substituieren, falls einige Werte doch dynamisch sein sollen.

JSON-Heredocs und das $-Zeichen

JSON enthält oft Strings mit &#36;-Zeichen — etwa Templates, regex-Patterns oder Mustache-Variablen für ein nachgelagertes Tool. In einem normalen Heredoc würde Bash diese als Variablen interpretieren und meist durch leere Strings ersetzen. Zitiertes Heredoc (&lt;&lt;'EOF') ist die einzige sichere Variante. Auch innerhalb von JSON-Heredocs unbedingt darauf achten, dass weder &#36;() noch Backticks unbeabsichtigt expandiert werden — sonst landet plötzlich ein Befehlsergebnis in der JSON-Struktur.

Heredoc-Block bricht set -e bei Fehler im Empfänger nicht

Wenn der Empfänger-Befehl scheitert (z. B. psql mit Syntaxfehler in der Query), greift set -e zwar — aber der Heredoc selbst ist zu diesem Zeitpunkt schon vollständig an psql übergeben. Du verlierst nicht die Eingabe, aber du hast keine Rückmeldung, welche Zeile im Heredoc das Problem war. Für komplexe Inline-Skripte besser eine echte Datei mit Zeilennummern erzeugen oder Fehlerausgaben des Empfängers gezielt mit 2>&amp;1 | tee mitschneiden.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Shell-Scripting

Zur Übersicht