12.6Substitutionsmechanismen
Der Begriff Substitutionsmechanismus klingt abstrakt und kompliziert. Die Grundidee besteht darin, dass mit Sonderzeichen gebildete Kommandos durch ihre Ergebnisse ersetzt werden. Im einfachsten Fall bedeutet das, dass bei der Auswertung des Kommandos ls *.tex die Zeichenkombination *.tex durch die Liste der passenden Dateien – etwa buch.tex command.tex – ersetzt wird. Das Kommando ls bekommt also nicht *.tex zu sehen, sondern eine Liste mit realen Dateinamen.
Kommando |
Funktion |
---|---|
? |
genau ein beliebiges Zeichen |
* |
beliebig viele (auch null) beliebige Zeichen (aber keine .*-Dateien!) |
** |
alle Dateien und Verzeichnisse, auch aus allen Unterverzeichnissen (ab bash 4.0 mit shopt -s globstar) |
[abc] |
eines der angegebenen Zeichen |
[a-f] |
ein Zeichen aus dem angegebenen Bereich |
[!abc] |
keines der angegebenen Zeichen |
[^abc] |
wie oben |
~ |
Abkürzung für das Heimatverzeichnis |
. |
aktuelles Verzeichnis |
.. |
übergeordnetes Verzeichnis |
ab{1,2,3} |
liefert ab1 ab2 ab3. |
a{1..4} |
liefert a1 a2 a3 a4. |
$[3*4] |
arithmetische Berechnungen |
`kommando` |
ersetzt das Kommando durch sein Ergebnis. |
$(kommando) |
wie oben, alternative Schreibweise |
kommando "zeichen" |
verhindert die Auswertung aller Sonderzeichen außer $. |
kommando 'zeichen' |
wie oben, aber noch restriktiver (keine Variablensubstitution) |
Tabelle 12.4Substitutionsmechanismen
Das Ziel dieses Abschnitts ist es, die wichtigsten Mechanismen bei der Interpretation der Kommandozeile vorzustellen (siehe Tabelle 12.4): Jokerzeichen dienen zur Bildung von Dateinamen, geschweifte Klammern zum Zusammensetzen von Zeichenketten, eckige Klammern zur Berechnung arithmetischer Klammern, umgekehrte Apostrophe zur Kommandosubstitution etc.
Ein Substitutionsmechanismus wird an dieser Stelle unterschlagen, nämlich die sogenannte Parametersubstitution. Damit können Sie in Variablen gespeicherte Zeichenketten analysieren und verändern. Die generelle Syntax lautet ${var__text}, wobei var der Name einer Variablen ist, __ für ein oder zwei Sonderzeichen steht und text das Suchmuster oder eine Defaulteinstellung enthält. Details zum Umgang mit Variablen sowie zu dem in diesem Zusammenhang eingesetzten Substitutionsmechanismus folgen im Abschnitt 12.10, »Variablen in bash-Scripts«.
Wenn Sie rm *.bak eingeben und das Kommando rm tatsächlich alle Dateien löscht, die mit .bak enden, dann ist dafür die bash verantwortlich. Die Shell durchsucht das aktuelle Verzeichnis nach passenden Dateien und ersetzt *.bak durch die entsprechenden Dateinamen.
Als Jokerzeichen sind ? (genau ein beliebiges Zeichen) und * (beliebig viele (auch null) beliebige Zeichen) erlaubt. Die Zeichenkette [a,b,e-h]* steht für Dateinamen, die mit einem der Zeichen a, b, e, f, g oder h beginnen. Wenn als erstes Zeichen innerhalb der eckigen Klammern ^ oder ! angegeben wird, dann sind alle Zeichen außer den angegebenen Zeichen zulässig. ~ kann als Abkürzung für das Heimatverzeichnis verwendet werden.
Die Funktion von Sonderzeichen können Sie einfach mit dem folgenden echo-Kommando testen. Das erste Kommando liefert alle Dateien und Verzeichnisse im Wurzelverzeichnis. Das zweite Kommando schränkt die Ausgabe auf Dateien und Verzeichnisse ein, die mit den Buchstaben a-f beginnen:
Da die Bildung der Dateinamen nicht durch das jeweilige Programm, sondern durch die bash erfolgt, sehen die Resultate manchmal anders aus, als Sie es wahrscheinlich erwarten würden. So kann ls * zu einer schier endlosen Liste von Dateien führen, auch wenn sich im aktuellen Verzeichnis nur wenige Dateien befinden. Dem Kommando ls wird nach der Expansion von * eine Liste aller Dateien und Verzeichnisse übergeben.
ls wiederum zeigt bei Verzeichnissen nicht einfach deren Namen, sondern den ganzen Inhalt dieser Verzeichnisse an. Wenn Sie nur eine einfache Liste aller Dateien und Verzeichnisse haben möchten, müssen Sie die Option -d verwenden. Sie verhindert, dass der Inhalt der in der Parameterzeile angegebenen Verzeichnisse angezeigt wird.
Wenn Sie ein Feedback haben möchten, wie die bash intern funktioniert, können Sie set -x ausführen. Die bash zeigt dann vor der Ausführung jedes weiteren Kommandos an, wie die Kommandozeile ausgewertet wird (mit allen eventuell voreingestellten Optionen und mit den expandierten Dateinamen).
Standardmäßig berücksichtigt * keine Dateien oder Verzeichnisse, die mit einem Punkt beginnen (also »verborgen« sind). Wenn Sie diese auch erfassen möchten, müssen Sie mit shopt die bash-Option dotglob setzen:
Die Zeichenkombination ** erfasst rekursiv alle Dateien und Verzeichnisse. Aus Kompatibilitätsgründen zu älteren bash-Versionen ist diese in Version 4 eingeführte Neuerung standardmäßig nicht aktiv. Wenn Sie sie nutzen möchten (z.B. in einem Script), müssen Sie mit shopt -s die bash-Option globstar setzen.
bash setzt aus Zeichenketten, die in geschweiften Klammern angegeben werden, alle denkbaren Zeichenkettenkombinationen zusammen. Die offizielle Bezeichnung für diesen Substitutionsmechanismus lautet Klammererweiterung (Brace Expansion). Aus teil{1,2a,2b} wird teil1 teil2a teil2b. Klammererweiterungen können den Tippaufwand beim Zugriff auf mehrere ähnliche Dateinamen oder Verzeichnisse reduzieren. Gegenüber Jokerzeichen wie * und ? haben sie den Vorteil, dass auch noch nicht existierende Dateinamen gebildet werden können (etwa für mkdir).
Aufzählungen können Sie elegant in der Schreibweise {a..b} formulieren, wobei a und b wahlweise Zahlen oder Buchstaben sein dürfen. Die folgenden Beispiele erklären die Funktionsweise besser als jede Beschreibung:
bash ist normalerweise nicht in der Lage, Berechnungen auszuführen. Wenn Sie 2+3 eingeben, weiß die Shell nicht, was sie mit diesem Ausdruck anfangen soll. Wenn Sie innerhalb der Shell eine Berechnung ausführen möchten, müssen Sie den Ausdruck in eckige Klammern setzen und ein $-Zeichen voranstellen:
Innerhalb der eckigen Klammern sind die meisten aus der Programmiersprache C bekannten Operatoren erlaubt: + - * / für die vier Grundrechenarten, % für Modulo-Berechnungen, == != < <= > und >= für Vergleiche, << und >> für Bitverschiebungen, ! && und || für logisches NICHT, UND und ODER etc. Alle Berechnungen werden für 32-Bit-Integerzahlen ausgeführt (Zahlenbereich zwischen +/-2147483648). Wenn einzelne Werte aus Variablen entnommen werden sollen, muss ein $-Zeichen vorangestellt werden.
Eine alternative Möglichkeit, Berechnungen durchzuführen, bietet das Kommando expr. Dabei handelt es sich um ein eigenständiges Linux-Kommando, das unabhängig von bash funktioniert.
Die Kommandosubstitution ermöglicht es, ein Kommando innerhalb der Kommandozeile durch dessen Ergebnis zu ersetzen. Dazu muss dieses Kommando zwischen zwei `-Zeichen eingeschlossen werden. Eine alternative Schreibweise lautet $(kommando). Diese Schreibweise ist vorzuziehen, weil sie erstens die Verwirrung durch die Verwendung von drei verschiedenen Anführungszeichen mindert (", ' und `) und zweitens verschachtelt werden kann.
Das so gekennzeichnete Kommando wird also durch sein Ergebnis ersetzt. Diese Substitution ermöglicht den verschachtelten Aufruf mehrerer Kommandos, wobei ein Kommando sein Ergebnis an das andere Kommando übergibt. Die beiden folgenden, gleichwertigen Kommandos verdeutlichen diesen sehr leistungsfähigen Mechanismus:
Durch das obige Kommando wird zuerst find /usr/share -name '*README*' ausgeführt. Das Ergebnis dieses Kommandos ist eine Liste aller Dateien im Verzeichnis /usr/share, in denen die Zeichenkette README vorkommt. Diese Liste wird nun anstelle des find-Kommandos in die Kommandozeile eingesetzt. Die Kommandozeile lautet dann beispielsweise:
Dieses Kommando führt zum folgenden Ergebnis:
Dieses Ergebnis wäre durch eine einfache Pipe mit dem |-Zeichen nicht möglich. ls erwartet keine Eingaben über die Standardeingabe und ignoriert daher auch die Informationen, die find über die Pipe liefert. Das folgende Kommando zeigt daher nur einfach den Inhalt des aktuellen Verzeichnisses an. Die Ergebnisse von find werden nicht angezeigt!
Es gibt aber eine andere Lösung, die ohne Kommandosubstitution auskommt: Durch die Zuhilfenahme des Kommandos xargs werden Daten aus der Standardeingabe an das nach xargs angegebene Kommando weitergeleitet:
Ein wesentlicher Vorteil von xargs besteht darin, dass es kein Größenlimit für die zu verarbeitenden Daten gibt. Gegebenenfalls ruft xargs das Kommando mehrfach auf und übergibt die aus der Standardeingabe kommenden Daten in mehreren Schritten. Die Kommandosubstitution ist hingegen durch die maximale Größe einer Kommandozeile – üblicherweise mehrere Tausend Zeichen – begrenzt.
Die Weitergabe von Dateinamen führt zu Problemen, wenn die Dateinamen Leerzeichen enthalten. Diese Probleme können Sie umgehen, indem Sie an find die Option -print0 übergeben und an xargs die Option --null. Das folgende Kommando setzt bei allen Verzeichnissen das execute-Bit:
Da in der bash praktisch jedes Zeichen mit Ausnahme der Buchstaben und Ziffern irgendeine besondere Bedeutung hat, scheint es so gut wie unmöglich zu sein, diese Zeichen in Zeichenketten oder Dateinamen zu verwenden. Das Problem kann auf zwei Arten gelöst werden: Entweder wird dem Sonderzeichen ein Backslash \ vorangestellt, oder die gesamte Zeichenkette wird in Apostrophe oder Anführungszeichen gestellt. Durch die Angabe von Apostrophen können Sie also beispielsweise eine Datei mit dem Dateinamen ab* $cd löschen:
Beachten Sie bitte den Unterschied zwischen ' zur Kennzeichnung von Zeichenketten und ` zur Kommandosubstitution!
Anführungszeichen haben eine ähnliche Wirkung wie Apostrophe. Sie sind allerdings weniger restriktiv und ermöglichen die Interpretation einiger Sonderzeichen wie $ \ und `. In Zeichenketten, die in Anführungszeichen gestellt sind, werden daher Shell-Variablen mit vorangestelltem $-Zeichen ausgewertet:
Das Kommando liefert als Ergebnis die Zeichenkette »Das ist der Zugriffspfad:«, gefolgt vom Inhalt der Shell-Variablen PATH. Wenn statt der Anführungszeichen einfache Apostrophe verwendet werden, wird die gesamte Zeichenkette unverändert durch echo ausgegeben.