<< Vorheriger Befehl Nächster Befehl >>

Bash-Befehle und Bash-Programmierung

© Klaus Gerhardt, 08.2005

(Copyright, Nutzungsbedingungen, Haftungsausschluss, s.u.)


Wildcards und die Klammererweiterung

Wildcards - Jokerzeichen bei Dateioperationen verwenden
Klammererweiterung - (Brace Expansion) Mechanismus zur Zeichenkettenbildung
globbing, file globbing, shell globbing, pathname expansion, Pfadnamenerweiterung
Vorsicht Falle! Rekursives Verhalten von Bash-Befehlen

Wildcards - Jokerzeichen bei Dateioperationen verwenden

Bei den Wildcards handelt es sich um Jokerzeichen für Dateioperationen wie Suchen, Kopieren, Verschieben und Löschen. Mit Hilfe der Wildcards kann man Dateien, die einem bestimmten Muster entsprechen, zusammenfassen. Nicht verwechseln darf man die Wildcards mit den Regulären Ausdrücken, wie sie z.B. mit grep verwendet werden. Beide sind sich recht ähnlich, verfolgen aber andere Zwecke. Wildcards verwendet man für Dateioperationen. Mit Regulären Ausdrücken sucht man nach Mustern in Textdateien.

Man unterscheidet in Linux zwischen regulären und versteckten Dateien. Die versteckten Dateien beginnen mit einem Punkt ".".

Achtung! Auch wenn die Linux-Wildcards grosse Ähnlichkeiten mit den DOS-Wildcards haben, gibt es gravierende Unterschiede, wie aus den unten stehenden Beschreibungen zu erstehen ist.

Zu den unten stehenden Wildcards habe ich gleich noch einige erklärende Beispiele hinzugefügt.

Wildcard Beschreibung
? Ersetzt ein beliebiges Zeichen.
* Ersetzt beliebig viele oder Null Zeichen und ist gleichzeitig die Wildcard für alle regulären Dateien.
Die Dateien dürfen nicht mit einem Punkt "." beginnen, allerdings dürfen sie ab dem 2. Zeichen beliebig viele Punkte enthalten.
xyz* Beginnt mit xyz, danach beliebig viele Zeichen.
.* Alle versteckten Dateien, d.h. die Datei beginnt mit einem Punkt.
{*,.[!.]*} Diese Kombination aus Klammererweiterung und Wildcards berücksichtigt alle regulären und alle versteckten Dateien und verhindert gleichzeitig, dass der Vorgang auf höheren Verzeichnisebenen übergreift. Im unten stehenden Beispiel 2, wird auf diese Problematik noch einmal genauer eingegangen.
*.* Die Datei muss mindestens einen Punkt enthalten, kann aber auch mehrere Punkte enthalten.
abc*.* Beginnt mit abc, danach mindestens ein Punkt.
[abc] In den eckigen Klammern stehen Platzhalter für genau ein Zeichen, hier a, b oder c.
[o-u] w.v., genau ein Zeichen, in diesem Fall aus dem Bereich der Kleinbuchstaben, von o-u.
[A-Z] w.v., hier alle Grossbuchstaben von A-Z. Damit sind dann Kleinbuchstaben, Zahlen und alle sonstigen Zeichen ausgeschlossen.
[A-Z]7 A7 B7 ... Z7
[A-Za-z]dat Adat Bdat ... Zdat adat bdat ... zdat
[!x] Nicht x.
[^x] Nicht x.
[!abc] Keines der Zeichen aus dem Zeichenvorrat in der eckigen Klammer.
[abc]de Kann sein: ade bde cde.
~ Joker für das Home-Verzeichnis.

Achtung! Die Ausdrücke mit den Wildcards müssen ggf. in Anführungszeichen gesetzt werden bzw. bash-eigene Zeichen müssen mit einem Backslash "\" maskiert werden.

Beispiel 1: In diesem Beispiel werden mit touch und der Klammererweiterung 3 Dateien angelegt und anschliessend wird die Ausgabe des Befehls ls mit Hilfe von Wildcards gefiltert.

/tmp/test # touch {l,m,r}aus ; ls       . .. laus maus raus /tmp/test # ls [ml]aus       laus maus /tmp/test # ls [!m]aus       laus raus

Beispiel 2: Hier geht es um die Problematik des ".*" Ausdruckes. Dieser umfasst nämlich nicht nur ".*" sondern auch "..*". Und dies ist die Verzeichnisebene des darüberliegenden Verzeichnisses. Im folgenden Beispiel werde ich dies zuerst mit dem Befehl ls demonstrieren.

Zuerst die Ausgangssituation. Wir haben das Verzeichnis /tmp/test mit einer versteckten und 3 regulären Dateien, sowie das Unterverzeichnis uv1 mit 2 versteckten und 2 regulären Dateien.

harry /tmp/test> tree -a       . |-- .kraus |-- laus |-- maus |-- raus `-- uv1 |-- .eins |-- .zwei |-- drei `-- vier

Ein Wechsel nach uv1. Ein einfacher ls zeigt die beiden reguären Dateien an:

harry /tmp/test/uv1> ls       drei vier

Ein ls .* führt dann aber zu diesem Ergebnis: Es werden die beiden versteckten Dateien aus uv1 angezeigt, aber auch die regulären Dateien aus uv1 da der "." für das aktuelle Verzeichnis steht und auch noch die Dateien aus dem darüberliegenden Verzeichnis /tmp/test welches durch ".." repräsentiert wird.

harry /tmp/test/uv1> ls .*       .eins .zwei .: drei vier ..: laus maus raus uv1

Wenn man diese Situation jetzt auf einen Befehl überträgt der rekursiv arbeitet, also von Verzeichnis zu Vezeichnis weiter klettert, führt dies zu einem unerwünschten Ergebnis. Im Falle von rm -r .* werden Dateien aus dem Verzeichnisbaum gelöscht die gar nicht dafür vorgesehen waren. Im Extremfall der ganze Verzeichnisbaum. Bei einem cp -R .* ist dies nicht ganz so dramatisch, aber immer noch ärgerlich.

Deshalb verwendet man diesen Ausdruck:

{*,.[!.]*}

Diese Kombination aus Klammererweiterung und Wildcards berücksichtigt alle regulären und alle versteckten Dateien und verhindert gleichzeitig, dass der Vorgang auf höhere Verzeichnisebenen übergreift.

Im Beispiel sieht man, dass dies auch zum gewünschten Ergebnis führt.

harry /tmp/test/uv1> ls {*,.[!.]*}       .eins .zwei drei vier

Auf den Kopierbefehl übertragen, sieht das ganze so aus:

cp -R {*,.[!.]*} /zielverzeichnis

Und hier der Ausdruck noch einmal genau erklärt:

{*,.[!.]*}

Die geschweiften Klammern bewirken eine Klammererweiterung. Die einzelnen Ausdrücke sind durch Komma voneinander getrennt. D.h. wir haben hier 2 Ausdrücke:

*

und

.[!.]*

Der erste Ausdruck ist klar. Zum zweiten Ausdruck:

Die Datei muss mit einem Punkt beginnen. Das nächste Zeichen darf kein Punkt sein. Danach folgen beliebig viele oder Null Zeichen.

Weiteres zu Wildcards siehe man 1 bash, dort nach "Pattern Matching" suchen (Fundstelle bei mir Zeile 1203).

Siehe auch: Klammererweiterung

Zum Seitenanfang


Klammererweiterung (Brace Expansion) - Mechanismus zur Zeichenkettenbildung

Bei der Klammererweiterung erweitert die bash die Ausdrücke in den geschweiften Klammern zu mehreren Zeichenfolgen. Vor und hinter den Klammern stehen optionale Zeichenfolgen, preample bzw. postscript genannt.

Zur Erklärung ein Beispiel:

touch adresse{1,2,3}

bash erweitert den Ausdruck zu

touch adresse1 adresse2 adresse3

Die Ausdrücke können natürlich auch mehrere Zeichen umfassen:

touch haus{1a,23c,110f}

wird zu

touch haus1a haus23c haus110f

Und Klammererweiterungen können ineinander verschachtelt werden:

/tmp/test # touch adresse{1{a,b},2{x,z},3{1,2}} ; ls       . .. adresse1a adresse1b adresse2x adresse2z adresse31 adresse32

Und auch noch Zeichen hinter der rechten Klammer haben (postscripts):

/tmp/test # touch haus{1,2,3}a ; ls       . .. haus1a haus2a haus3a

Achtung! Wenn man, wie hier im Beispiel unten, das Komma hinter den letzten Ausdruck schreibt, besteht eine Zeichenfolge nur aus der preample und dem postscript, sofern vorhanden.

/tmp/test # touch adresse{1,2,3,} ; ls       . .. adresse adresse1 adresse2 adresse3

Weiteres siehe: man 1 bash, dort suchen nach "brace expansion".

Siehe auch: touch

Zum Seitenanfang

globbing, file globbing, shell globbing, pathname expansion, Pfadnamenerweiterung

Wenn die bash als Dateimuster eine Wildcard, bzw. Kombinationen von Wildcards vorfindet, wird aus diesem Muster eine Dateiliste erstellt. Oder anders ausgedrückt das Muster wird zu einer Dateiliste expandiert. Dieser Vorgang heisst "pathname expansion" oder "globbing", manchmal auch "file globbing" oder "shell globbing" genannt. Der deutsche Begriff dafür ist Pfadnamenerweiterung.

Beispiele:

Das Testverzeichnis enthält 3 Dateien, wie hier mit "ls *" zu sehen ist:

klaus@athlon:/tmp/klaus/test> ls * bar foo foobar

Wenn ich jetzt "echo ls *" ausführe, kann ich das globbing beobachten:

klaus@athlon:/tmp/klaus/test> echo ls * ls bar foo foobar

Siehe auch: man 1 bash, dort nach "pathname expansion" suchen.

Zum Seitenanfang

Vorsicht Falle! Rekursives Verhalten von Bash-Befehlen

Viele bash-Befehle haben die Option -r bzw. -R um rekursiv zu arbeiten. Manche Befehle arbeiten standard mässig rekursiv (z.B. tar). Rekursiv heisst: der Befehl wirkt nicht nur auf das aktuelle Verzeichnis und die darin enthaltenen Dateien, sondern auf alle weiteren Unterverzeichnisse und die darin enthaltenen Dateien. Allerdings ist dies eine Falle. Denn nur mit der Wildcard "*" funktioniert bei den meisten Befehlen die rekursive Abarbeitung korrekt. Mit allen anderen Dateimustern nicht. Eine Ausnahme ist der Befehl find. Diesen muss man deshalb zu Hilfe nehmen, wenn man ein korrektes rekursives Verhalten erreichen will.

Warum ist dieses Kapitel hier angesiedelt? Weil es mit dem globbing zusammenhängt. In den folgenden Beispielen wird das rekursive Verhalten mit den Befehlen chmod, grep, tar und cp dargestellt. Bei dem Befehl chmod erkläre ich dann auch was es mit dem globbing zu tun hat.

Am Beispiel chmod:

Das ist die Ausgangssituation, dargestellt mit tree und find/ls:

~/test> tree . |-- foo |-- fxx |-- fxx.html `-- test.html |-- bar |-- bar.html |-- test2.html | |-- bar | `-- bar.html `-- testx |-- bar `-- bar.html

~/test> find * -ls 941615 4 drwxr-xr-x 2 klaus users 4096 Okt 8 11:29 foo 941616 0 -rw-r--r-- 1 klaus users 0 Okt 8 11:52 fxx 941617 0 -rw-r--r-- 1 klaus users 0 Okt 8 11:32 fxx.html 941614 4 drwxr-xr-x 4 klaus users 4096 Okt 8 12:08 test.html 941618 0 -rw-r--r-- 1 klaus users 0 Okt 8 11:33 test.html/bar 941619 0 -rw-r--r-- 1 klaus users 0 Okt 8 11:33 test.html/bar.html 941620 4 drwxr-xr-x 2 klaus users 4096 Okt 8 11:33 test.html/test2.html 941621 0 -rw-r--r-- 1 klaus users 0 Okt 8 11:33 test.html/test2.html/bar 941622 0 -rw-r--r-- 1 klaus users 0 Okt 8 11:33 test.html/test2.html/bar.html 941623 4 drwxr-xr-x 2 klaus users 4096 Okt 8 12:08 test.html/testx 941624 0 -rw-r--r-- 1 klaus users 0 Okt 8 12:08 test.html/testx/bar 941625 0 -rw-r--r-- 1 klaus users 0 Okt 8 12:08 test.html/testx/bar.html

Und das passiert wenn ich dem Befehl chmod echo voranstelle. Man sieht also die Dateiliste die von der bash aus dem Muster "*", mit Hilfe des globbing, erstellt wird.

~/test> echo chmod -R 777 *.html chmod -R 777 fxx.html test.html

Und das ist das Ergebnis wenn ich den Befehl ausgeführt habe:

~/test> chmod -R 777 *.html ~/test> find * -ls 941615 4 drwxr-xr-x 2 klaus users 4096 Okt 8 11:29 foo 941616 0 -rw-r--r-- 1 klaus users 0 Okt 8 11:52 fxx 941617 0 -rwxrwxrwx 1 klaus users 0 Okt 8 11:32 fxx.html 941614 4 drwxrwxrwx 4 klaus users 4096 Okt 8 12:08 test.html 941618 0 -rwxrwxrwx 1 klaus users 0 Okt 8 11:33 test.html/bar 941619 0 -rwxrwxrwx 1 klaus users 0 Okt 8 11:33 test.html/bar.html 941620 4 drwxrwxrwx 2 klaus users 4096 Okt 8 11:33 test.html/test2.html 941621 0 -rwxrwxrwx 1 klaus users 0 Okt 8 11:33 test.html/test2.html/bar 941622 0 -rwxrwxrwx 1 klaus users 0 Okt 8 11:33 test.html/test2.html/bar.html 941623 4 drwxrwxrwx 2 klaus users 4096 Okt 8 12:08 test.html/testx 941624 0 -rwxrwxrwx 1 klaus users 0 Okt 8 12:08 test.html/testx/bar 941625 0 -rwxrwxrwx 1 klaus users 0 Okt 8 12:08 test.html/testx/bar.html

Das ist doch ein sehr merkwürdiges Verhalten. Es werden die Zugriffsrechte aller Dateien und Verzeichnisse, im aktuellen Verzeichnis, auf die das Muster *.html zutrifft geändert, sowie auch die aller Dateien in den darunterliegenden Verzeichnissen. Hier spielt das Muster aber keine Rolle mehr. Es werden die Zugriffrechte bei allen Dateien und darunter liegenden Verzeichnissen geändert. Der Befehl ist in dieser Weise angewandt also völlig nutzlos.

Der Befehl arbeitet also im aktuellen Verzeichnis korrekt. Ab dem Verzeichnis test.html arbeitet er dann recursiv, aber ohne das Muster zu beachten. Denn chmod selbst hat mit dem Dateimuster nichts zu tun. Die Dateien die chmod bearbeiten soll werden durch das globbing von der Bash zur Verfügung gestellt.

Alternative hierzu: Verwendung von chmod zusammen mit find. Beispiele hierzu findet man im Projekt 4.

Am Beispiel grep:

~/test> tree . |-- fxx |-- fxx.html `-- test.html |-- bar.html |-- bar.txt

~/test> grep -r "" *.html fxx.html: test.html/bar.html: test.html/bar.txt:

Obwohl als Dateimuster *.html angegeben ist, findet grep auch das Suchmuster in test.html/bar.txt.

Auch hier muss man wiederum grep mit find kombinieren. Ein Beispiel hierzu ist bei dem Befehl grep beschrieben.

Am Beispiel tar:

~/test> tree . |-- foo |-- fxx |-- fxx.html `-- test.html |-- bar.html |-- bar.txt |-- test2.html | |-- bar | `-- bar.html `-- testx |-- bar `-- bar.html

Mit dem Datemuster "*" arbeitet tar korrekt. D.h. alle Dateien werden im Archiv aufgenommen:

~/test> tar -cvf file1 * foo/ fxx fxx.html test.html/ test.html/bar.html test.html/test2.html/ test.html/test2.html/bar test.html/test2.html/bar.html test.html/testx/ test.html/testx/bar test.html/testx/bar.html test.html/bar.txt

Mit dem Muster "*.html" funktioniert auch tar nicht korrekt. Es werden auch Dateien gepackt die nicht auf das Muster passen.

~/test> tar -cvf file2 *.html fxx.html test.html/ test.html/bar.html test.html/test2.html/ test.html/test2.html/bar test.html/test2.html/bar.html test.html/testx/ test.html/testx/bar test.html/testx/bar.html test.html/bar.txt

Auch hier kommt wieder der Befehl find zu Anwendung um ein einwandfreies rekursives Verhalten zu erreichen. Ein Beispiel ist bei dem Befehl tar beschrieben.

Beispiel mit cp:

Zum Abschluss noch ein Beispiel mit dem Befehl cp. Aus dem unten stehenden Verzeichnisbaum sollen alle Dateien mit dem Muster "*.html" kopiert werden.

~/test> tree . |-- fxx |-- fxx.html `-- test.html |-- bar.html |-- bar.txt |-- test2.html | |-- bar | `-- bar.html `-- testx |-- bar `-- bar.html

Der Kopierbefehl:

~/test> cp -r *.html /tmp/klaus/test

Das Ergebnis:

klaus@athlon:/tmp/klaus/test>tree . |-- fxx.html `-- test.html |-- bar.html |-- bar.txt |-- test2.html | |-- bar | `-- bar.html `-- testx |-- bar `-- bar.html

Auch hier wurden wieder Dateien kopiert die dem Muster nicht entsprechen.

Fazit: Bei den meisten Befehlen funktioniert die Rekursion nur mit dem Dateimuster "*". Wenn man mit einem anderen Muster arbeitet muss man den Befehl find zur Hilfe nehmen. Denn dieser arbeitet von Natur aus "echt" rekursiv.

Siehe auch: find.

Zum Seitenanfang

Copyright, Nutzungsbedingungen, Haftungsausschluss

© Klaus Gerhardt • http://linuxseiten.kg-it.de

  • Dieses Dokument darf in unveränderter Form vervielfältigt und weitergereicht werden, sofern diese Nutzung privat erfolgt.
  • Der Name des Autors muss genannt werden, sowie die in diesem Absatz festgelegten Nutzungsbedingungen.
  • Eine kommerzielle Nutzung ist nur mit der Zustimmung des Autors erlaubt.
  • Die Vervielfältigung entsprechend den o.g. Bedingungen ist sowohl in elektronischer Form, als auch auf Papier zulässig.

Der Autor haftet weder für die Anwendung, der in diesem Dokument beschriebenen Verfahren, noch für die Anwendung von beigefügten Shell-Skripten. Die Haftung liegt alleine beim Anwender.

Shellskripte, die mit einem Link von diesem Dokument aus heruntergeladen werden können, bilden einen Teil dieses Dokumentes und müssen unverändert zusammen mit diesem Dokument weitergereicht werden. Man kann diese Shellskripte jedoch auch separat verwenden. Dann darf man sie nach belieben verändern.