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.
cat <<EOF
Erste Zeile
Zweite Zeile
EOFErste Zeile
Zweite ZeileDer 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.
name="Michael"
host="$(hostname)"
cat <<EOF
Hallo $name,
du arbeitest gerade auf $host.
Heute ist $(date +%A).
EOFHallo 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.
if true; then
cat <<-EOF
Erste Zeile
Zweite Zeile
EOF
fiErste Zeile
Zweite ZeileEntscheidend: 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.
cat <<'EOF'
Pfad: $HOME
Datum: $(date)
Backtick: `whoami`
EOFPfad: $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.
wc -w <<< "drei kleine worte"3Klassisch 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:
wert="alpha:beta:gamma"
IFS=":" read -r a b c <<< "$wert"
echo "a=$a, b=$b, c=$c"a=alpha, b=beta, c=gammaDas 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:
text="error: file not found"
grep -o "error" <<< "$text"errorHeredoc 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:
cat <<EOF | grep "wichtig"
unwichtig 1
wichtig 2
unwichtig 3
EOFAuch in einer Subshell oder Process Substitution funktioniert das wie erwartet:
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.
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;
}
}
EOFBeachte 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.
user_id=42
psql -d shop <<EOF
SELECT name, email
FROM users
WHERE id = $user_id
ORDER BY created_at DESC;
EOFMulti-Line-Echo ohne printf-Akrobatik: cat <<EOF ist die lesbarste Variante, mehrere Zeilen am Stück auszugeben — gerade für Hilfetexte oder Banner.
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.
: <<'COMMENT'
Dieser ganze Block ist nur Doku.
Hier könnten ausführliche Erklärungen
stehen, ohne dass jede Zeile mit # beginnt.
COMMENTString in read mit Herestring ist das idiomatische Pattern, einen einzelnen String in mehrere Variablen zu zerlegen — ohne Subshell-Falle:
line="2026-05-04 ERROR Datenbank nicht erreichbar"
IFS= read -r datum level rest <<< "$line"
echo "Datum: $datum"
echo "Level: $level"
echo "Rest: $rest"Datum: 2026-05-04
Level: ERROR
Rest: Datenbank nicht erreichbarDas 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 <<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 < <(cat <<EOF ... EOF). Dann läuft die Schleife in der Haupt-Shell.
Unterschied `<<<` vs. Pipe — same shell vs. Subshell
cmd <<< "$str" läuft in derselben Shell, echo "$str" | cmd startet cmd in einer Subshell. Für read ist das der entscheidende Unterschied: read x <<< "$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 $var, $(...) und Backticks ausgewertet — was für nginx-Configs, awk-Snippets oder Python-Code im Heredoc tödlich ist. Punktuell hilft Backslash: \$var bleibt literal, \\ wird zu \. Bei mehr als zwei, drei Stellen lohnt sich die zitierte Form <<'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 $-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 (<<'EOF') ist die einzige sichere Variante. Auch innerhalb von JSON-Heredocs unbedingt darauf achten, dass weder $() 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>&1 | tee mitschneiden.
Weiterführende Ressourcen
Externe Quellen
- Bash-Manpage: Here Documents (man7.org) — Offizielle Referenz zu
<<und<<- - Bash-Manpage: Here Strings — Beschreibung der
<<<-Syntax - GNU Bash-Handbuch: Redirections — Vollständige Doku aller Umleitungsformen mit Beispielen
- Bash Pitfalls (mywiki.wooledge.org) — Häufige Fehler rund um Quoting und Heredocs
Verwandte Artikel
- Streams und Pipes — stdin, stdout, stderr und ihre Umleitung
- Skript-Grundlagen — Shebang, Permissions und der robuste Skript-Header
- Process Substitution — Befehls-Outputs als Pseudo-Dateien
- Funktionen — Wiederverwendbare Code-Bausteine im Skript
- Parameter — Argumente und Optionen sauber verarbeiten