Die drei Klammern [] {} () und die BASH – Ein Streifzug durch die syntaktische Vielfalt der Shell

Autor: DerSchneider


Einleitung

Die Bourne-Again-Shell, kurz Bash, ist eines der langlebigsten und zugleich mächtigsten Werkzeuge der UNIX-Welt. Wer täglich mit ihr arbeitet, kommt an einem Phänomen nicht vorbei, das Einsteiger regelmäßig zur Verzweiflung treibt: die Vielzahl der Klammerzeichen und ihre scheinbar willkürliche Verwendung. Runde Klammern (), geschweifte Klammern {} und eckige Klammern [] – drei Zeichen, drei Welten, unzählige Bedeutungen.

Die Idee zu diesem Artikel entstand bei einem Shell-Vortrag von Leyrer im Jahr 2023: Es gibt so viele verschiedene Klammer-Typen in der Shell, und so viele verschiedene Einsatzmöglichkeiten und Bedeutungen. Doch was auf den ersten Blick wie syntaktische Willkür wirkt, offenbart bei näherer Betrachtung eine durchdachte Systematik – ein Erbe aus den Anfängen der UNIX-Entwicklung, das bis heute die Arbeitsweise von Systemadministratoren und Entwicklern prägt.

Dieser Artikel beleuchtet die drei Klammerarten in ihrer ganzen Tiefe: ihre Funktionen, ihre Unterschiede, ihre historischen Hintergründe und die Fallstricke, die selbst erfahrene Anwender immer wieder übersehen.


Geschweifte Klammern {} – Die Kunst der Expansion

Die geschweiften Klammern sind das Werkzeug der Wahl, wenn es darum geht, Zeichenketten zu erzeugen, bevor die Shell überhaupt daran denkt, Dateien zu suchen oder Befehle auszuführen. Die sogenannte Brace Expansion ist ein Mechanismus zur Generierung beliebiger Zeichenketten mit gemeinsamem Präfix und Suffix.

Sequenzen und Listen

Die einfachste Form ist die Erzeugung von Zahlen- und Buchstabenfolgen:

bash

echo {1..10}        # 1 2 3 4 5 6 7 8 9 10
echo {01..10}       # 01 02 03 04 05 06 07 08 09 10
echo {A..H}         # A B C D E F G H
echo {H..A}         # H G F E D C B A

Die Bash erlaubt auch Inkremente:

bash

echo {0..15..2}     # 0 2 4 6 8 10 12 14
echo {1..15..2}     # 1 3 5 7 9 11 13 15
echo {H..A..2}      # H F D B

Besonders nützlich ist die Kombination von Präfix und Suffix:

bash

touch file_{1..10}.txt          # Erzeugt file_1.txt bis file_10.txt
cp file.txt{,.bak}              # Kopiert file.txt nach file.txt.bak
rm /a/long/path/{foo,bar}       # Entfernt beide Dateien[reference:2]

Die Oktalfalle

Eine der häufigsten Fehlerquellen betrifft die Behandlung von Zahlen mit führender Null. Während die Brace Expansion selbst führende Nullen problemlos verarbeitet, gilt das nicht für die arithmetische Expansion:

bash

echo $((010 + 1))   # 9, nicht 11!

Der Grund ist historisch: UNIX entstand auf PDP-Rechnern, die intern mit Oktalzahlen arbeiteten. Eine führende Null signalisiert bis heute ein oktales Zahlensystem – eine Konvention, die in der Bash-Arithmetik bis in die Gegenwart fortwirkt.

Die Reihenfolge der Expansion

Ein entscheidendes Detail: Die Brace Expansion findet vor allen anderen Expansionen statt, noch vor der Variablensubstitution. Das führt zu überraschenden Effekten:

bash

a=1
b=4
echo {$a..$b}       # Funktioniert nicht wie erwartet

Die Variablen werden erst nach der Brace Expansion aufgelöst, sodass die Sequenz nicht gebildet werden kann. Wer dennoch variable Grenzen verwenden möchte, muss auf andere Konstrukte wie die seq– oder eval-Mechanismen zurückgreifen.


Eckige Klammern [] – Das Erbe des Globings

Während die geschweiften Klammern unabhängig vom Dateisystem arbeiten, sind die eckigen Klammern untrennbar mit der Welt der Dateinamen verbunden. Sie sind Teil des sogenannten Globbings – der Mustererkennung für Dateinamen, die neben * und ? die dritte Säule der Shell-Patterns bildet.

Zeichenklassen und Bereiche

Die Syntax ist einfach: Alles zwischen den eckigen Klammern repräsentiert eine Menge von Zeichen, von denen genau eines matchen muss:

bash

ls file[A-C]        # Matcht fileA, fileB, fileC
ls file[a-c]        # Matcht filea, fileb, filec
ls file[1-3]        # Matcht file1, file2, file3
ls file[ACE]        # Matcht fileA, fileC, fileE[reference:6]

Negation ist durch ^ oder ! möglich:

bash

ls file[^A-C]       # Matcht alles außer fileA, fileB, fileC
ls file[!A-C]       # Gleiche Bedeutung[reference:7]

Die Kollationssequenz-Falle

Ein Thema, das in der Bash-Version 5.0 eine überraschende Wendung nahm, ist die Behandlung von Zeichenbereichen in Abhängigkeit von der System-Lokalisierung. Traditionell interpretierte die Bash [A-Z] als die ASCII-Zeichen von A bis Z – eine Erwartung, die sich in den Köpfen ganzer Generationen von Administratoren festgesetzt hatte.

Mit Bash 5.0 änderte sich dies. Die Option globasciiranges ist seitdem standardmäßig aktiviert. Das bedeutet: [A-Z] folgt nun der Kollationssequenz der aktuellen Locale. In manchen Locales können dadurch auch Kleinbuchstaben oder Umlaute in den Bereich fallen – mit potenziell verheerenden Folgen für Skripte, die auf das alte Verhalten angewiesen sind.

Die Lösung: Entweder die Option deaktivieren (shopt -u globasciiranges) oder die Locale temporär auf C setzen:

bash

( LC_ALL=C; echo [A-Z]* )

Glob versus Brace – Ein entscheidender Unterschied

Die eckigen Klammern operieren auf dem Dateisystem. Sie matchen existierende Dateien – sie erzeugen keine neuen. Die geschweiften Klammern hingegen generieren Zeichenketten, unabhängig davon, ob entsprechende Dateien existieren. Dieser Unterschied ist fundamental und erklärt viele der Missverständnisse, die Einsteiger (und manchmal auch Profis) in die Irre führen.


Runde Klammern () – Subshells und mehr

Die runden Klammern sind die vielseitigsten der drei Klammerarten. Sie treten in mehreren völlig unterschiedlichen Kontexten auf, die jeweils eigene Regeln und Eigenheiten mit sich bringen.

Kommandogruppierung in der Subshell

Die offensichtlichste Verwendung: Ein in runde Klammern gefasster Befehlsblock wird in einer eigenen Subshell ausgeführt:

bash

(cd /tmp && ls)     # Wechselt in /tmp, listet auf, kehrt zurück

Der entscheidende Effekt: Alle Variablenzuweisungen innerhalb der Subshell bleiben ohne Wirkung auf die aufrufende Shell:

bash

a=1
(a=2)
echo $a             # Gibt 1 aus

Dieses Verhalten ist keine Schwäche, sondern eine bewusste Entscheidung. Die Subshell bietet eine isolierte Umgebung für temporäre Operationen – ein Konzept, das in der Shell-Programmierung von unschätzbarem Wert ist.

Arithmetische Expansion

Die doppelten runden Klammern ((...)) sind der moderne Weg für arithmetische Operationen:

bash

echo $((1 + 1))     # 2
echo $((010 + 1))   # 9 (oktale Falle!)

Die einfache runde Klammer in der Form $(...) dient der Befehlssubstitution – der Ausführung eines Befehls und der Ersetzung durch seine Ausgabe.

Die C-ähnliche Schleife

Eine weitere Sonderform sind die doppelten runden Klammern in Schleifenköpfen, die eine an C angelehnte Syntax ermöglichen:

bash

for ((i=0; i<10; i++)); do
    echo $i
done

Diese Form ist deutlich flexibler als die klassische for i in {0..9}-Schleife, da sie variable Grenzen und komplexe Bedingungen erlaubt.


Geschweifte Klammern als Kommandogruppe {}

Neben der Brace Expansion kennen die geschweiften Klammern eine zweite, völlig eigenständige Verwendung: die Kommandogruppierung im aktuellen Shell-Kontext:

bash

{ sleep 10; cat; } | program

Im Gegensatz zur runden Klammer wird keine Subshell erzeugt. Variablenzuweisungen bleiben erhalten. Das hat Konsequenzen:

bash

a=1
{ a=2; }
echo $a             # Gibt 2 aus

Wichtig: Die geschweiften Klammern sind Reserved Words, keine Operatoren. Sie müssen durch Leerzeichen oder andere Metazeichen vom umgebenden Text getrennt werden. Ein abschließendes Semikolon (oder ein Zeilenumbruch) ist zwingend erforderlich:

bash

{ echo foo; }       # Korrekt
{echo foo;}         # Falsch – Syntaxfehler

Der Unterschied zu den runden Klammern ist subtil, aber bedeutsam. Während (...) eine neue Umgebung schafft, bleibt {...} in der bestehenden – ein Unterschied, der bei Pipelines und Umleitungen entscheidend wird.


Prozesssubstitution <() und >() – Die versteckte Klammer

Eine der mächtigsten, aber auch am häufigsten übersehenen Funktionen der Bash ist die Prozesssubstitution. Sie ermöglicht es, die Ausgabe eines Befehls wie eine Datei zu behandeln:

bash

diff <(ls dir1) <(ls dir2)

Die Syntax < (list) oder > (list) erzeugt einen named pipe oder nutzt /dev/fd, um dem aufrufenden Befehl einen Dateinamen zu übergeben. Der listete Befehl läuft asynchron.

Ein klassisches Beispiel: der Vergleich zweier sortierter Dateien ohne temporäre Dateien:

bash

comm -3 <(sort a | uniq) <(sort b | uniq)     # Zeigt einzigartige Zeilen[reference:22]

Warnung: Die Prozesssubstitution ist nicht POSIX-kompatibel. In Skripten, die strikte POSIX-Konformität erfordern, oder im POSIX-Modus der Bash (set -o posix) ist sie deaktiviert.

Ebenso kritisch: Zwischen < oder > und der öffnenden Klammer darf kein Leerzeichen stehen – sonst interpretiert die Shell den Ausdruck als Umleitung.


Die Geschichte der Ausrufezeichen – Historische Expansion !

Kein Artikel über die Bash-Syntax wäre vollständig ohne einen Blick auf die History Expansion. Ursprünglich aus der C-Shell (csh) übernommen, ermöglicht sie den Zugriff auf die Befehlshistorie:

bash

!!                  # Letzter Befehl
!$                  # Letztes Argument des letzten Befehls
!*                  # Alle Argumente des letzten Befehls
!^                  # Erstes Argument
!:2                 # Zweites Argument[reference:29]

Die History Expansion wird nach dem Einlesen der Zeile, aber vor der Wortzerlegung durchgeführt. Sie ist in interaktiven Shells standardmäßig aktiviert.

Die Verbindung zu den Klammern? Die History Expansion verwendet das Ausrufezeichen – ein Zeichen, das in Kombination mit Klammern und anderen Sonderzeichen für zusätzliche Verwirrung sorgen kann. Die gute Nachricht: Sie kann mit set +H deaktiviert werden.


Die große Übersicht – Ein Vergleich

KlammerHauptfunktionSubshell?ReihenfolgeBesonderheit
{1..10}Brace ExpansionNeinVor allen anderenGeneriert Zeichenketten
{a,b,c}Brace ExpansionNeinVor allen anderenBeliebige Listen
[a-z]GlobbingNeinBeim Dateinamen-MatchingArbeitet auf dem Dateisystem
$(cmd)BefehlssubstitutionJaVor der AusführungErsetzt durch Ausgabe
((...))ArithmetikNeinVor der AusführungC-ähnliche Syntax
(...)KommandogruppeJaZur LaufzeitEigene Umgebung
{...}KommandogruppeNeinZur LaufzeitAktuelle Umgebung
<(cmd)ProzesssubstitutionJa (asynchron)ParallelBefehl als Datei

Fazit und Ausblick

Die drei Klammern der Bash – []{} und () – sind mehr als nur syntaktische Spielerei. Sie sind Ausdruck einer Design-Philosophie, die sich über fünf Jahrzehnte UNIX-Geschichte entwickelt hat. Jede Klammer hat ihre eigene Aufgabe, ihre eigene Geschichte und ihre eigenen Fallstricke.

Die geschweiften Klammern erzeugen Zeichenketten, bevor die Shell überhaupt an Dateien denkt. Die eckigen Klammern durchkämmen das Dateisystem nach Mustern. Die runden Klammern öffnen neue Umgebungen – sei es für temporäre Berechnungen, isolierte Befehlsausführungen oder die elegante Verkettung von Prozessen.

Wer diese Unterschiede versteht, wird nicht nur weniger Fehler machen, sondern auch die volle Kraft der Bash entfesseln können. Die Shell ist kein überholtes Relikt – sie ist ein lebendiges Zeugnis der Informatikgeschichte, das sich durch kluge Erweiterungen und behutsame Modernisierung bis heute behauptet.

Die Bash-Version 5.0 mit ihrer globasciiranges-Option ist ein Beispiel für diesen schmalen Grat zwischen Tradition und Fortschritt. Die Prozesssubstitution zeigt, wie selbst nicht-standardisierte Erweiterungen zu unverzichtbaren Werkzeugen werden können.

Die drei Klammern sind geblieben – und sie werden bleiben. Denn sie sind nicht nur Syntax, sie sind die DNA der Shell.


Quellen

  1. GNU Bash Reference Manual: Brace Expansion (Section 3.5.1)
  2. GNU Bash Reference Manual: Command Grouping (Section 3.2.5.3)
  3. GNU Bash Reference Manual: Process Substitution (Section 3.5.6)
  4. GNU Bash Reference Manual: History Interaction (Section 9.3)
  5. Linux Journal: Bash Brace Expansion (Mitch Frazier, 2008)
  6. Linux Journal: Bash Process Substitution (Mitch Frazier, 2008)
  7. Linux Handbook: Using Brace Expansion in Bash Shell (Abhishek Prakash, 2022)
  8. KodeKloud: Square – Globbing with Square Brackets
  9. GPN24: Die drei Klammern [] {} () und die BASH (Vortrag von Leyrer, Martin Schulte und Harald)
  10. Unix Stack Exchange: Diskussionen zu globasciiranges und Collating Sequences

Kommentar abschicken

Das hast du vielleicht verpasst