Globbing in Bash: extglob, globstar und nullglob praktisch nutzen
AI generated
$_
#!/
Bash · Globbing · Dateisystem · Pattern
Globbing in Bash
extglob, globstar und nullglob praktisch einsetzen, statt an leeren Treffern zu scheitern

Bashs eingebautes Globbing kann weit mehr als das einfache Sternchen-Muster vieler Anfängerskripte: Mit shopt -s extglob stehen erweiterte Pattern wie Alternativen und Negation zur Verfügung, globstar aktiviert rekursives ** Matching über ganze Verzeichnisbäume, und nullglob verhindert den klassischen Fehler, bei dem ein Muster ohne Treffer wörtlich als Dateiname weitergereicht wird.

16 Min. Lesezeit shopt · extglob · globstar · nullglob Bash 4.x · 5.x · Linux

1. Was Globbing ist und wo die Standard-Wildcards enden

Globbing bezeichnet die Fähigkeit der Shell, Muster wie *.log oder file?.txt selbst zu expandieren, bevor ein Befehl überhaupt aufgerufen wird, im Unterschied zu regulären Ausdrücken, die von einzelnen Programmen wie grep interpretiert werden. Das Standard-Repertoire von Bash umfasst * für eine beliebige Zeichenfolge, ? für genau ein Zeichen und Zeichenklassen wie [abc] oder [0-9], was für die meisten alltäglichen Dateiauswahlen bereits ausreicht.

Sobald die Anforderungen komplexer werden, etwa mehrere alternative Endungen in einem einzigen Muster oder ein rekursiver Abstieg durch Unterverzeichnisse, stößt das Standard-Globbing an seine Grenzen. Genau hier setzen die drei shopt-Optionen extglob, globstar und nullglob an, die zusammen aus Bashs Globbing ein deutlich mächtigeres Werkzeug machen, ohne dass ein externes Programm wie find aufgerufen werden muss.

2. extglob: erweiterte Pattern mit ?(...), @(...) und !(...)

Mit shopt -s extglob aktiviert Bash eine Reihe erweiterter Musteroperatoren, die syntaktisch an reguläre Ausdrücke erinnern, aber weiterhin reines Globbing bleiben. @(muster1|muster2) matcht genau eine der angegebenen Alternativen, ?(muster) matcht das Muster null- oder einmal, *(muster) null- oder mehrmals, +(muster) ein- oder mehrmals, und !(muster) negiert das Muster, also alles, was gerade nicht darauf passt.

In der Praxis ist @(...) der mit Abstand meistgenutzte Operator, etwa um mehrere Dateiendungen in einem Rutsch zu erfassen, während !(...) sich anbietet, um gezielt alles außer einer bestimmten Datei oder einem bestimmten Muster zu selektieren, was ohne extglob nur mit einer Kombination aus find und grep -v möglich wäre.


#!/usr/bin/env bash
set -euo pipefail
shopt -s extglob

# Match multiple alternative extensions in one pattern
for f in *.@(jpg|jpeg|png|webp); do
  echo "Image: $f"
done

# Match everything EXCEPT a specific file
for f in !(README.md); do
  echo "Not the README: $f"
done

# Match one-or-more repetitions of a sub-pattern
for f in backup+([0-9]).tar.gz; do
  echo "Numbered backup: $f"
done

3. globstar: rekursives ** Matching über Verzeichnisbäume

Standardmäßig matcht * in Bash keine Verzeichnistrenner, ein Muster wie src/*.php steigt also nicht automatisch in Unterordner ab. Mit shopt -s globstar ändert sich das Verhalten von zwei aufeinanderfolgenden Sternchen ** grundlegend: Ein Muster wie src/**/*.php matcht dann jede PHP-Datei in src und in beliebig tief verschachtelten Unterverzeichnissen, funktional vergleichbar mit find src -name '*.php', aber ohne einen externen Prozessaufruf.

Ein wichtiger Unterschied zu find ist, dass globstar bei sehr großen Verzeichnisbäumen alle passenden Pfade zunächst vollständig in den Speicher expandiert, bevor der Befehl überhaupt startet, während find die Ergebnisse als Stream liefert. Bei einigen hunderttausend Dateien kann das spürbar mehr Speicher und Zeit kosten, weshalb globstar sich vor allem für Projektverzeichnisse überschaubarer Größe eignet, nicht für das rekursive Durchsuchen ganzer Dateisysteme.


#!/usr/bin/env bash
set -euo pipefail
shopt -s globstar

# Match every .php file at any depth under src/
for f in src/**/*.php; do
  echo "PHP file: $f"
done

# Combine globstar with extglob for recursive multi-extension matching
shopt -s extglob
for f in src/**/*.@(php|phtml); do
  echo "PHP or phtml: $f"
done

4. nullglob: der Fallstrick leerer Treffer ohne diese Option

Ohne nullglob gibt Bash ein Muster, das keine einzige Datei matcht, wörtlich unverändert zurück, statt eine leere Liste zu liefern. In einer Schleife wie for f in *.bak; do ...; done führt das dazu, dass der Schleifenkörper trotzdem genau einmal ausgeführt wird, mit der buchstäblichen Zeichenkette *.bak als Wert von $f, was fast immer ein Programmierfehler ist und in Skripten mit unzureichender Prüfung zu verwirrenden Folgefehlern führt, etwa einem Löschbefehl, der auf eine nicht existierende Datei namens *.bak angewendet wird.

Mit shopt -s nullglob expandiert ein Muster ohne Treffer stattdessen zu einer leeren Liste, sodass die Schleife bei keinem Treffer korrekterweise gar nicht erst durchläuft. Diese Option gehört in praktisch jedes produktive Bash-Skript, das mit Glob-Mustern über Dateilisten iteriert, weil sie den häufigsten und gleichzeitig am schwersten zu debuggenden Globbing-Fehler von vornherein ausschließt.


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

cd /tmp/empty-dir-without-bak-files 2>/dev/null || mkdir -p /tmp/empty-dir-without-bak-files && cd "$_"

echo "Without nullglob:"
for f in *.bak; do
  echo "  Loop ran with: $f"   # prints literally "*.bak" -- misleading!
done

shopt -s nullglob
echo "With nullglob:"
for f in *.bak; do
  echo "  Loop ran with: $f"   # never prints, loop body is skipped entirely
done
echo "Loop correctly skipped when nothing matched"

5. Alle drei Optionen kombiniert: ein robustes Deployment-Skript

In der Praxis werden extglob, globstar und nullglob selten einzeln verwendet, sondern meist gemeinsam am Anfang eines Skripts aktiviert, weil sie sich gegenseitig ergänzen: globstar liefert die rekursive Tiefe, extglob die Ausdrucksstärke bei der Musterwahl, und nullglob die Sicherheit gegen leere Treffer. Ein typisches Einsatzbeispiel ist ein Deployment-Skript, das rekursiv alle Konfigurationsdateien mit bestimmten Endungen sucht und dabei Testdateien gezielt ausschließt.

Wichtig ist, alle shopt-Aufrufe möglichst am Skriptanfang zu bündeln und zu dokumentieren, welche Optionen aktiv sind, weil ein Leser, der mit den Standardeinstellungen von Bash vertraut ist, ein Muster wie !(*.test.php) ohne den Hinweis auf extglob gar nicht als gültiges Bash-Pattern erkennt.


#!/usr/bin/env bash
set -euo pipefail
shopt -s extglob globstar nullglob

readonly CONFIG_DIR="config"

# Recursively find all .yaml/.yml files, excluding anything with .test. in the name
for f in "$CONFIG_DIR"/**/!(*.test).@(yaml|yml); do
  echo "Deploying config: $f"
done

6. dotglob und failglob: zwei verwandte, oft übersehene Optionen

Neben den drei Kernoptionen lohnt sich ein Blick auf zwei verwandte shopt-Schalter: dotglob sorgt dafür, dass Muster wie * auch versteckte Dateien erfassen, deren Name mit einem Punkt beginnt, was standardmäßig ausgeschlossen ist, weil sonst jedes *-Muster ungewollt . und .. mit einschließen würde. Wer Konfigurationsdateien wie .env oder .gitignore in ein Glob-Muster einbeziehen will, kommt an dotglob nicht vorbei.

failglob ist das genaue Gegenteil von nullglob: Statt bei keinem Treffer eine leere Liste zu liefern, bricht Bash mit einer Fehlermeldung ab, sobald ein Muster nicht matcht. Das eignet sich für Skripte, in denen ein fehlender Treffer tatsächlich ein harter Fehlerzustand sein soll, etwa wenn ein erwartetes Build-Artefakt zwingend vorhanden sein muss, bevor der nächste Schritt starten darf.

7. Sicherheit: Glob-Ergebnisse und nicht vertrauenswürdige Dateinamen

Ein oft übersehenes Risiko beim Globbing ist, dass ein expandiertes Muster Dateinamen liefern kann, die mit einem Bindestrich beginnen, etwa -rf. Wird ein solcher Name ungeschützt an einen Befehl wie rm $files weitergereicht, kann er versehentlich als Kommandozeilenoption statt als Dateiname interpretiert werden. Deshalb sollte jede Variable, die aus einem Glob-Ergebnis stammt, immer in Anführungszeichen verwendet werden, und bei Aufrufen wie rm ist der Präfix --, der alles Folgende zwingend als Dateiname statt als Option behandelt, eine zusätzliche Absicherung.

Ein zweites Risiko betrifft Dateinamen mit eingebetteten Leerzeichen oder Zeilenumbrüchen: Wird das Ergebnis eines Glob-Musters ohne Anführungszeichen in eine Schleifenvariable geschrieben und dann per Word-Splitting weiterverarbeitet, reißt Bash einen einzelnen Dateinamen mit Leerzeichen fälschlich in mehrere Wörter auseinander. Die sichere Variante iteriert direkt über das Array, das for f in *.log; do ... done ohnehin liefert, und verzichtet auf jede zusätzliche Zwischenspeicherung als einfacher, ungequoteter String.

8. Performance: Globbing vs. find bei großen Verzeichnisbäumen

Für kleine bis mittlere Projektverzeichnisse mit wenigen tausend Dateien ist globstar-basiertes Globbing in der Regel schnell genug und hat den Vorteil, komplett ohne externen Prozessaufruf auszukommen, was besonders in Schleifen mit vielen Wiederholungen spürbar ist. Bei Verzeichnisbäumen mit hunderttausenden Dateien oder tief verschachtelten Netzwerk-Dateisystemen zeigt find dagegen klare Vorteile, weil es Ergebnisse als Stream liefert und zusätzliche Filter wie Änderungsdatum oder Dateigröße direkt einbauen kann, ohne den kompletten Ergebnissatz vorher in den Speicher zu laden.

Eine bewährte Faustregel ist, globstar für Projektverzeichnisse und Deployment-Skripte zu verwenden, deren Größe man kennt und kontrolliert, und auf find umzusteigen, sobald ein Skript auf beliebig großen, fremdverwalteten Verzeichnisbäumen laufen soll, etwa bei einer generischen Backup- oder Aufräumroutine, die auf unterschiedlichen Servern mit unbekannter Dateianzahl läuft.

9. Wann welche Glob-Option zum Einsatz kommt

Die Wahl der richtigen Kombination hängt vom konkreten Anwendungsfall ab: einfache Musterlisten mit mehreren Endungen brauchen nur extglob, rekursive Suchen über Unterverzeichnisse brauchen zusätzlich globstar, und jede Schleife, die mit einem Glob-Muster über eine potenziell leere Ergebnismenge iteriert, sollte grundsätzlich nullglob aktivieren, um den klassischen Fehler mit dem wörtlichen Musterstring zu vermeiden.

Option Aktiviert mit Effekt Typischer Einsatz
extglob shopt -s extglob Erweiterte Pattern: @(), ?(), !(), +() Mehrere Endungen, Negation von Mustern
globstar shopt -s globstar ** matcht rekursiv über Verzeichnisse Projektweite Dateisuche ohne find
nullglob shopt -s nullglob Muster ohne Treffer wird zu leerer Liste Sichere Schleifen über Glob-Ergebnisse
dotglob shopt -s dotglob * erfasst auch versteckte Dateien Konfigurationsdateien wie .env erfassen
failglob shopt -s failglob Kein Treffer bricht mit Fehler ab Erwartete Dateien zwingend voraussetzen

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

Globbing in Bash: Das Wichtigste auf einen Blick

extglob

Aktiviert @(), ?(), !(), *() und +() für erweiterte Musteralternativen und Negation, syntaktisch nah an erweiterten regulären Ausdrücken.

globstar

Zwei Sternchen ** matchen rekursiv über beliebig tiefe Unterverzeichnisse, ohne einen externen find-Aufruf zu benötigen.

nullglob

Verhindert den klassischen Fehler, bei dem ein Muster ohne Treffer wörtlich als Dateiname in der Schleife landet.

Kombination

extglob, globstar und nullglob werden meist gemeinsam am Skriptanfang aktiviert und ergänzen sich gegenseitig.

11. FAQ: Globbing in Bash: Das Wichtigste auf einen Blick

1Was macht shopt -s extglob genau?
extglob aktiviert erweiterte Musteroperatoren wie @(a|b), ?(muster), *(muster), +(muster) und !(muster), mit denen Alternativen, optionale Wiederholungen und Negation in einem einzigen Glob-Muster formuliert werden können.
2Wie unterscheidet sich globstar von normalem *?
Ein einzelnes * matcht keinen Verzeichnistrenner und steigt daher nicht in Unterordner ab. Nach shopt -s globstar matcht ** rekursiv über beliebig tief verschachtelte Verzeichnisse hinweg.
3Warum sollte ich nullglob in jedem Skript aktivieren?
Ohne nullglob gibt ein Muster ohne Treffer sich selbst als wörtliche Zeichenkette zurück, was Schleifen fälschlich einmal durchlaufen lässt. nullglob sorgt stattdessen für eine korrekt leere Ergebnisliste.
4Kann ich extglob, globstar und nullglob gleichzeitig aktivieren?
Ja, alle drei lassen sich in einem einzigen shopt-Aufruf setzen: shopt -s extglob globstar nullglob. Sie schließen sich nicht gegenseitig aus und ergänzen sich in der Praxis meist sehr gut.
5Ist globstar performanter als find?
Für kleine bis mittlere Verzeichnisbäume ist globstar oft schneller, weil kein externer Prozess gestartet wird. Bei sehr großen Verzeichnisbäumen liefert find dagegen Ergebnisse als Stream und ist speicherschonender.
6Was ist der Unterschied zwischen nullglob und failglob?
nullglob liefert bei keinem Treffer eine leere Liste, das Skript läuft normal weiter. failglob bricht stattdessen mit einer Fehlermeldung ab, sobald ein Muster keine Datei matcht.
7Warum erfasst mein *-Muster keine versteckten Dateien?
Bash schließt Dateien, deren Name mit einem Punkt beginnt, standardmäßig von * aus, um versehentliches Matching von . und .. zu verhindern. shopt -s dotglob schaltet dieses Verhalten ab.
8Funktionieren extglob-Muster auch in case-Statements?
Ja, sobald extglob aktiv ist, lassen sich dieselben erweiterten Muster wie @(...) oder !(...) auch als case-Pattern verwenden, was case-Verzweigungen deutlich kompakter macht.
9Bleiben shopt-Einstellungen über das ganze Skript hinweg aktiv?
Ja, eine mit shopt -s gesetzte Option gilt für die gesamte laufende Shell-Sitzung beziehungsweise das gesamte Skript, bis sie explizit mit shopt -u wieder deaktiviert wird.
10Muss ich globstar für einfache Verzeichnisangaben wie src/*.php aktivieren?
Nein, für Muster ohne rekursiven Abstieg reicht das Standard-Globbing völlig aus. globstar wird ausschließlich für das doppelte Sternchen ** gebraucht, das über Unterverzeichnisse hinweg matcht.