Heredocs und Herestrings in Bash: fortgeschrittene Muster
AI generated
$_
#!/
Bash · Skripting · I/O-Umleitung · Linux
Heredocs und Herestrings
fortgeschrittene Muster fuer mehrzeiligen Text in Bash-Skripten

Ein Heredoc erlaubt es, mehrzeiligen Text direkt im Skript unterzubringen, ohne jede Zeile einzeln zu escapen. Wer aber den Unterschied zwischen <

15 Min. Lesezeit < Bash 4.x · 5.x · POSIX sh

1. Warum Heredocs und Herestrings existieren

Ein Heredoc (kurz fuer here document) leitet mehrzeiligen Text direkt aus dem Skript in die Standardeingabe eines Befehls um, ohne dass jede Zeile in Anfuehrungszeichen stehen oder Zeilenumbrueche mit \n nachgebildet werden muessen. Ohne Heredoc muesste ein mehrzeiliger String entweder aus vielen einzelnen echo-Aufrufen zusammengesetzt oder als eine einzige, kaum lesbare Zeile mit \n-Escapes geschrieben werden.

Eine Herestring loest ein verwandtes, aber kleineres Problem: einen bereits vorhandenen, meist einzeiligen String als Standardeingabe an einen Befehl zu uebergeben, ohne dafuer extra echo "$var" | befehl zu schreiben und damit eine zusaetzliche Pipe und einen zusaetzlichen Prozess zu erzeugen. Beide Konstrukte loesen also das gleiche Grundproblem, Text als Eingabe bereitzustellen, nur fuer unterschiedliche Textmengen und mit unterschiedlicher Syntax.

2. <

Die Grundform befehl < beginnt mit einem frei waehlbaren Delimiter direkt nach <<, ueblicherweise EOF (End Of File) oder END, und alles bis zur Zeile, die exakt nur diesen Delimiter enthaelt, wird als Standardeingabe an den Befehl weitergereicht. Der Name des Delimiters selbst hat keine besondere Bedeutung fuer Bash, er muss lediglich eindeutig sein und darf nicht zufaellig im Textkoerper als eigene Zeile auftauchen.

Eine haeufige Fehlerquelle ist, dass die schliessende Delimiter-Zeile exakt und ohne fuehrende Leerzeichen (ausser bei <<-EOF, siehe naechster Abschnitt) und ohne nachfolgende Zeichen dastehen muss. Ein einzelnes Leerzeichen nach EOF oder ein Kommentarzeichen in derselben Zeile fuehrt dazu, dass Bash den Delimiter nicht erkennt und weiter auf das Zeilenende wartet, was ein Skript scheinbar unbegruendet haengen laesst.


cat <<EOF
Zeile eins
Zeile zwei mit Variable: $USER
EOF
# Ausgabe:
# Zeile eins
# Zeile zwei mit Variable: deploy

3. <<-EOF: fuehrende Tabs automatisch entfernen

Steht ein Heredoc eingerueckt innerhalb einer Funktion oder eines if-Blocks, sorgt die Variante <<-EOF mit einem zusaetzlichen Bindestrich dafuer, dass Bash fuehrende Tabulatorzeichen sowohl im Textkoerper als auch in der schliessenden Delimiter-Zeile automatisch entfernt, bevor der Text weitergereicht wird. Damit kann der Heredoc-Block optisch mit dem umgebenden Code mitwandern, statt an der linken Spalte zu kleben und den Lesefluss zu stoeren.

Wichtig ist die Einschraenkung auf Tabs: <<-EOF entfernt ausschliesslich fuehrende Tabulatorzeichen, keine Leerzeichen. Wer seinen Editor auf automatische Einrueckung mit Leerzeichen statt Tabs eingestellt hat, sieht optisch identisch aussehenden Code, bei dem <<-EOF aber wirkungslos bleibt und die Leerzeichen im ausgegebenen Text landen. Diese Falle ist besonders tueckisch, weil der Unterschied im Editor meist unsichtbar ist.


deploy_step() {
	if true; then
		cat <<-EOF
			Diese Zeile beginnt im Quelltext mit Tabs,
			landet aber ohne fuehrende Tabs in der Ausgabe.
		EOF
	fi
}
deploy_step

4. Herestring <<<"string": Eingabe ohne Pipe umleiten

Eine Herestring mit <<< uebergibt einen bereits vorhandenen String direkt als Standardeingabe, ohne einen zusaetzlichen Prozess fuer echo zu starten. grep muster <<< "$inhalt" ist funktional aequivalent zu echo "$inhalt" | grep muster, spart aber eine Pipe und eine Subshell, was insbesondere in Schleifen mit vielen Iterationen messbar schneller ist.

Der praktische Nutzen zeigt sich vor allem beim Fuellen von read-Aufrufen mit bereits vorhandenen Variablenwerten, etwa um einen String an Leerzeichen in mehrere Variablen aufzuteilen, ohne dafuer eine Pipe zu benoetigen, die eine eigene Subshell erzeugen und damit gesetzte Variablen nach dem Aufruf wieder verschwinden lassen wuerde.


version_string="2.4.8-p4"
IFS='.' read -r major minor patch <<< "$version_string"
echo "Major: $major, Minor: $minor"
# Major: 2, Minor: 4

5. Den Delimiter quoten: Variablenexpansion im Heredoc verhindern

Standardmaessig expandiert Bash innerhalb eines Heredocs Variablen, Command Substitution und Backslash-Escapes, genau wie in doppelten Anfuehrungszeichen. cat < gefolgt von $HOME im Textkoerper ersetzt diesen Platzhalter also durch den tatsaechlichen Pfad. Das ist erwuenscht, wenn ein Skript eine Konfigurationsdatei mit dynamischen Werten fuellen soll, aber gefaehrlich, wenn der Heredoc-Inhalt ein Beispiel-Skript oder ein Literal-Template sein soll, das selbst $-Zeichen enthaelt.

Wird der Delimiter dagegen in einfache oder doppelte Anfuehrungszeichen gesetzt, etwa <<'EOF', deaktiviert Bash jede Expansion innerhalb des Heredocs vollstaendig, und der Text wird byte-genau so weitergereicht, wie er im Skript steht. Diese eine Zeichen-Entscheidung, quoted oder unquoted Delimiter, ist der wichtigste Hebel bei Heredocs und sollte bei jedem Heredoc bewusst getroffen werden, statt sich auf das Standardverhalten zu verlassen.


# Unquoted: $USER wird expandiert
cat <<EOF
Aktueller Nutzer: $USER
EOF

# Quoted: $USER bleibt Literal-Text, keine Expansion
cat <<'EOF'
Beispiel-Variable im Zielskript: $USER
EOF

6. Konfigurationsdateien aus Skripten erzeugen

Ein produktiver Einsatzzweck ist, eine mehrzeilige Konfigurationsdatei direkt aus einem Deployment-Skript zu schreiben, indem der Heredoc statt an cat mit einer Umleitung direkt an eine Zieldatei gesendet wird. Dabei entscheidet dieselbe Quoting-Regel wie im vorherigen Abschnitt, ob Variablen aus dem umgebenden Skript in die erzeugte Datei einfliessen sollen oder ob die Zieldatei selbst Platzhalter im $name-Format enthalten soll, die erst von einer anderen Software interpretiert werden.

Fuer Nginx- oder systemd-Unit-Dateien, die selbst $-Syntax verwenden koennen, ist ein gequoteter Delimiter fast immer die richtige Wahl, damit Bash diese Zeichen nicht faelschlich als eigene Variablen interpretiert. Fuer Templates, die bewusst mit Werten aus dem Skript wie einem Hostnamen oder einer Portnummer gefuellt werden sollen, ist der unquotierte Delimiter dagegen genau richtig.


#!/usr/bin/env bash
set -euo pipefail

app_port=8080
target="/etc/nginx/sites-available/app.conf"

# Quoted Delimiter fuer Nginx-eigene $-Variablen, aber $app_port
# aus dem Skript wird gezielt vorher interpoliert
cat > "$target" <<CONF
server {
  listen ${app_port};
  location / {
    proxy_pass http://127.0.0.1:${app_port};
    proxy_set_header Host \$host;
  }
}
CONF

7. Heredoc-Inhalt in eine Variable einfangen

Heredocs lassen sich mit Command Substitution kombinieren, um mehrzeiligen, dynamisch generierten Text nicht direkt an eine Datei oder ein Programm zu senden, sondern zunaechst in einer Variablen zu speichern und spaeter weiterzuverarbeiten, etwa fuer eine E-Mail-Vorlage oder eine Log-Nachricht, die noch formatiert werden muss.

Die Kombination text=$(cat < funktioniert genauso wie ein einfacher Heredoc, mit demselben Quoting-Verhalten fuer den Delimiter, nur dass die Ausgabe von cat statt auf die Standardausgabe des Terminals in die Variable text wandert. Wichtig dabei ist, dass Command Substitution einen abschliessenden Zeilenumbruch am Ende immer entfernt, was bei mehrzeiligem Text meist erwuenscht, aber gelegentlich ueberraschend ist.


hostname_local="$(hostname)"
report=$(cat <<EOF
Deployment-Bericht fuer $hostname_local
Zeitpunkt: $(date -Iseconds)
Status: erfolgreich
EOF
)
echo "$report"
mail -s "Deployment" ops@example.invalid <<< "$report"

8. Typische Stolperfallen bei Heredocs

Die haeufigste Falle ist ein unsichtbares Leerzeichen nach der schliessenden Delimiter-Zeile, das dazu fuehrt, dass Bash den Heredoc nicht als beendet erkennt und das Skript scheinbar grundlos haengt, bis es mit Ctrl+D oder Ctrl+C abgebrochen wird. Ein Editor mit sichtbaren Whitespace-Zeichen oder ein kurzer Blick mit cat -A skript.sh deckt diese Faelle zuverlaessig auf.

Die zweite haeufige Falle ist das Vermischen von Tabs und Leerzeichen bei <<-EOF: wenn ein Editor automatisch Tabs in Leerzeichen umwandelt, bleibt die Einrueckung optisch erhalten, aber <<-EOF entfernt sie nicht mehr, und die erzeugte Ausgabe enthaelt ungewollte fuehrende Leerzeichen, was besonders bei generierten YAML-Dateien zu schwer nachvollziehbaren Parse-Fehlern fuehrt.

9. Heredoc-Varianten im Vergleich

Die Wahl zwischen <, <<-EOF, gequotetem Delimiter und Herestring haengt davon ab, ob Variablen expandiert werden sollen, ob der Block eingerueckt im Quelltext steht und wie viel Text tatsaechlich uebergeben wird. Die folgende Tabelle fasst die Entscheidungskriterien zusammen.

Variante Variablenexpansion Fuehrende Tabs entfernt Typischer Einsatz
< Ja Nein Konfigurationsdatei mit dynamischen Werten
<<-EOF Ja Ja, nur Tabs Eingerueckter Heredoc in Funktionen
<<'EOF' Nein, Literal-Text Nein Beispiel-Skripte, Templates mit eigenem $-Syntax
<<<"string" Ja, vor der Uebergabe Entfaellt, einzeiliger String Vorhandene Variable ohne Pipe uebergeben

Mironsoft

Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur

Shell-Skripte, die in der Produktion zuverlässig laufen?

Wir analysieren bestehende Bash-Skripte, erkennen fragile Muster und ersetzen sie durch robuste Bash-Patterns: mit vollständiger Fehlerbehandlung, Logging und sicherer Parallelisierung für euren Deployment-Stack.

Code-Review

ShellCheck-Analyse und manuelle Prüfung auf kritische Bash-Pattern-Verstöße.

Refactoring

Fehlerbehandlung, Logging und sichere Dateioperationen nachrüsten.

CI-Integration

ShellCheck und BATS in Pipelines integrieren und Regressionstests aufbauen.

10. Zusammenfassung

Heredocs und Herestrings: Das Wichtigste auf einen Blick

Grundregel

Der Delimiter nach << bestimmt Beginn und Ende des Heredocs, die schliessende Zeile muss exakt und ohne Zusatzzeichen dastehen.

Quoting

Ein gequoteter Delimiter wie <<'EOF' deaktiviert jede Variablenexpansion, ein unquotierter Delimiter expandiert $VAR wie in doppelten Anfuehrungszeichen.

Einrueckung

<<-EOF entfernt nur fuehrende Tabs, keine Leerzeichen. Bei Leerzeichen-Einrueckung im Editor bleibt der Effekt aus.

Herestring

<<<"string" ersetzt echo var | befehl fuer bereits vorhandene, meist einzeilige Werte ohne zusaetzliche Pipe.

11. FAQ: Heredocs und Herestrings: Das Wichtigste auf einen Blick

1Was ist der Unterschied zwischen Heredoc und Herestring?
Ein Heredoc mit << leitet mehrzeiligen Text zwischen zwei Delimiter-Zeilen um. Eine Herestring mit <<< uebergibt einen bereits vorhandenen, meist einzeiligen String direkt als Eingabe.
2Warum haengt mein Skript nach einem Heredoc?
Meist steht ein unsichtbares Leerzeichen oder Zeichen nach der schliessenden Delimiter-Zeile. Bash erkennt den Delimiter dann nicht und wartet weiter auf Eingabe.
3Wie verhindere ich, dass Variablen im Heredoc expandiert werden?
Den Delimiter in Anfuehrungszeichen setzen, etwa <<'EOF' statt <
4Was macht <<-EOF anders als <
<<-EOF entfernt fuehrende Tabulatorzeichen im Textkoerper und in der schliessenden Zeile, sodass der Heredoc eingerueckt im Quelltext stehen kann. Leerzeichen werden davon nicht entfernt.
5Kann ich einen Heredoc direkt in eine Datei schreiben?
Ja, mit cat > datei <
6Warum ist eine Herestring schneller als echo var | befehl?
Weil echo var | befehl einen zusaetzlichen Prozess fuer echo sowie eine Pipe erzeugt, waehrend <<<"var" die Eingabe direkt ohne zusaetzlichen Prozess umleitet.
7Kann ich Heredoc-Inhalt in einer Variable speichern?
Ja, mit var=$(cat <
8Muss der Delimiter EOF heissen?
Nein, der Name ist frei waehlbar, solange er eindeutig ist und nicht zufaellig als eigene Zeile im Textkoerper vorkommt. EOF und END sind nur verbreitete Konventionen.
9Werden Backslash-Escapes im Heredoc verarbeitet?
Bei unquotiertem Delimiter ja, genau wie in doppelten Anfuehrungszeichen. Bei gequotetem Delimiter bleibt auch der Backslash ein Literal-Zeichen.
10Funktioniert <<-EOF auch mit Leerzeichen-Einrueckung?
Nein, <<-EOF entfernt ausschliesslich fuehrende Tabs. Bei Leerzeichen-Einrueckung im Editor landen die Leerzeichen unveraendert in der Ausgabe.