Es mag prefix-*
zum Beispiel so aussehen, als ob es einfach sein sollte, sich zu verwandeln prefix-1 prefix-2
, da wir es gewohnt sind, Verzeichnislisten sortiert zu sehen. Es stellt sich jedoch heraus, dass nur sehr wenige Dateisysteme tatsächlich sortierte Dateinamenlisten erstellen können und dass es keine Standard-API gibt, um nach sortierten Dateinamenlisten zu fragen.
Wenn ein Programm - wie zum Beispiel ls
oder bash
- eine Liste von Dateinamen benötigt, muss es die gesamte Verzeichnisliste lesen, die in zufälliger Reihenfolge erstellt wird (oft hängt die Reihenfolge mit der Erstellungszeit zusammen; manchmal) Es basiert auf einem Hash des Dateinamens, aber in keinem Fall ist es eine einfache alphabetische Reihenfolge. Um dies zu beheben prefix-*
, müssen Sie das gesamte Verzeichnis lesen und jeden Dateinamen anhand des Musters überprüfen. Da der kostspieligste Teil dieser Prozedur das Lesen des Verzeichnisses ist, spielt es keine Rolle, wie komplex das Muster ist oder wie viele Dateinamen mit dem Muster übereinstimmen.
Zusammenfassend wird die Pfadnamenerweiterung ("Auflösen von Globs") in einem großen Verzeichnis langsam sein. Das ist ein Grund, große Verzeichnisse zu vermeiden, und kein Grund, Globs zu vermeiden.
Es gibt aber noch einen anderen wichtigen Datenpunkt: Es prefix-{1,2}
handelt sich nicht um eine Pfadnamenerweiterung. Es handelt sich um eine " Klammererweiterung " und eine Erweiterung des Posix-Shell-Standards (obwohl fast alle Shells dies implementieren). Es gibt eine Reihe von Unterschieden zwischen der Erweiterung von geschweiften Klammern und der Erweiterung von Pfadnamen. Ein wichtiger und relevanter Unterschied besteht jedoch darin, dass die Erweiterung von geschweiften Klammern nicht von der Existenz von Dateien abhängt . Die Klammererweiterung ist eine einfache Zeichenfolgeoperation.
Folglich prefix-{1,2}
wird immer erweitert prefix-1 prefix-2
, unabhängig davon, ob diese Dateien vorhanden sind oder nicht. Das heißt, es kann erweitert werden, ohne das Verzeichnis zu lesen und ohne stat
eine Datei zu speichern. Klar, das wird schnell gehen. Aber es gibt einen Nachteil: Es gibt keine Möglichkeit festzustellen, ob das Ergebnis echten Dateien entspricht.
Betrachten Sie das folgende einfache Beispiel:
$ mkdir test && cd test
$ touch file1 file2 file4
$ ls file*
file1 file2 file4
$ ls file[1234]
file1 file2 file4
$ ls file{1,2,3,4}
ls: cannot access file3: No such file or directory
file1 file2 file4
Letzter Punkt: Die Pfadnamenerweiterung wird von der Shell durchgeführt, nicht von ls
. Mit der Pfadnamenerweiterung könnten wir genauso gut Folgendes verwenden echo
:
$ echo file*
file1 file2 file4
$ echo file[1234]
file1 file2 file4
Und echo
wird die Liste etwas schneller produzieren, weil alles echo
, was zu tun ist, seine Argumente zu drucken ist, während ls
(die die gleichen Argumente erhalten) zu stat
jedem Argument muss, um zu überprüfen, ob es eine Datei ist. Das stat
- was kein billiger Anruf ist - ist bei einer Pfadnamenerweiterung völlig überflüssig, da die Shell die Verzeichnisliste bereits zum Filtern der Dateiliste verwendet hat und daher jeder übergebene Dateiname ls
als vorhanden bekannt ist. (Es sei denn, der Glob hat überhaupt keine Dateien gefunden.)
Darüber hinaus ist Echo bash
integriert, sodass es aufgerufen werden kann, ohne einen untergeordneten Prozess zu erstellen.
Bei einer Klammererweiterung wird echo
jedoch nicht dasselbe Ergebnis erzielt:
$ echo file{1,2,3,4}
file1 file2 file3 file4
Wir könnten also verwenden ls
, um die Fehlerausgabe in den Bit-Bucket umzuleiten:
$ ls file{1,2,3,4}
file1 file2 file4
In diesem Fall sind die stat
Aufrufe nicht redundant, da die Shell die Dateinamen nie überprüft hat.
Sofern Ihre Verzeichnisse nicht wirklich riesig sind, wird dies keinen großen Unterschied machen und das Schreiben des Glob wird viel einfacher. Wenn Ihre Verzeichnisse sind wirklich riesig, sollten Sie sie in kleinere Unterverzeichnisse aufzuteilen.
Zum Beispiel anstelle von Pfaden wie:
/var/log/remote/serverX.domain.local/ps/ps2.log.2014-mm-dd.gz
Du könntest benutzen:
/var/log/remote/serverX/domain.local/ps/ps2.log.2014-mm-dd-gz
Wenn Sie die Protokolle für immer aufbewahren, möchten Sie möglicherweise das Jahr extrahieren, um eine unbegrenzte Vergrößerung des Verzeichnisses zu vermeiden:
/var/log/remote/2014/serverX/domain.local/ps/ps2.log.2014-mm-dd-gz
( 2014
wird absichtlich wiederholt.)
Das Sharding der Verzeichnisse ist normalerweise ein großer Gewinn, da es einen Mechanismus zur Optimierung des Globbing bietet. Wie oben erwähnt, kann die Shell nicht optimieren
/var/log/remote/server[2357].domain.local/ps/ps2.log.2014-10-*-gz
aber es kann optimieren
/var/log/remote/server[2357]/domain.local/ps/ps2.log.2014-10-*-gz
Im zweiten Fall muss server[2357]
nur ein Abgleich mit den Verzeichnisnamen durchgeführt werden. Anschließend muss ps2.log.2014-10-*-gz
nur noch ein Abgleich mit den Dateinamen in den abgeglichenen Verzeichnissen durchgeführt werden.
Die Shell-Erweiterung wird immer in einer bestimmten Reihenfolge ausgeführt. Klammererweiterung wird zuerst durchgeführt, Dateinamenerweiterung wird zuletzt durchgeführt.
Also ein Befehl wie
wird zunächst auf erweitert
dann wird die Dateinamen Expansion durchgeführt für
1*
,2*
und3*
. Bei jeder Erweiterung werden alle Dateinamen im Verzeichnis durchsucht und mit dem Muster verglichen.Je mehr Wörter und / oder Dateien sich im Verzeichnis befinden, desto langsamer wird dies. Auch in einem leeren Verzeichnis,
dauert fast fünf Sekunden auf meinem Computer. Dies ist überhaupt nicht überraschend, wenn man bedenkt, dass die Dateinamenerweiterung eine Million Mal durchgeführt wird ...
Eine viel schnellere Alternative besteht darin , beide Arten der Schalenerweiterung möglichst nicht zu kombinieren .
Der Befehl
Sucht nach denselben Dateinamen, verwendet jedoch ein einzelnes Muster. Dies dauert 33 Millisekunden auf meinem Computer.
Die Verwendung von eckigen Klammern anstelle von geschweiften Klammern bietet zusätzliche Vorteile:
Der erste Ansatz hat die Datei zweimal gefunden, da sie mit den Mustern
1*
und übereinstimmt13*
. Dies geschieht nicht bei "reiner" Dateinamenerweiterung.quelle