Wie erweitert eine Shell (z. B. Bash) Platzhaltermuster?

9

Angenommen, ein Verzeichnis enthält 100 Dateien, die mit dem Buchstaben 'a' beginnen.

grep <some string> a*Wie wird die Shell damit umgehen, wenn ich eine vom Terminal aus mache ?

Wird es den regulären Ausdruck erweitern, eine Liste aller Dateien erhalten, die mit a beginnen, und für jede dieser Dateien nacheinander grep? Oder gibt es einen anderen Weg?

Angenommen, ich habe ein Array der oben genannten Dateinamen, die mit 'a' beginnen. Wird es mehr oder weniger Zeit dauern, wenn ich eine for-Schleife schreibe und die Iteration selbst in einem Shell-Skript oder einem AC-Programm durchführe?

Harithski
quelle
7
Übrigens ist es globkein regulärer Ausdruck. Großer Unterschied.
Aaron D. Marasco

Antworten:

8

Erstens ein Nitpick: Ein String wie a*in der normalen Shell-Syntax ist ein Glob, der anders funktioniert als reguläre Ausdrücke.

In einer allgemeinen Übersicht erweitert der Shell-Interpreter (dh Bash) die Zeichenfolge a*zu einer Liste aller Dateinamen, die dem Muster entsprechen a*. Diese werden dann Teil der Befehlszeilenparameter für eine einzelne Instanz von grep(für die Programmierer werden alle erweiterten Wörter als separate Zeichenfolgen in das argvArgument von eingefügt main). Dieser einzelne grepBefehl analysiert dann die Argumente auf die von ihm gewählte Weise, und es liegt an grepdiesen Argumenten, sie als Dateinamen, Optionen, Optionsargumente, reguläre Ausdrücke usw. zu interpretieren und die entsprechenden Maßnahmen zu ergreifen. Alles erfolgt nacheinander (AFAIK keine grepImplementierung verwendet mehrere Threads).

Wenn Sie eine Schleife in einem Shell-Skript implementieren, um dasselbe zu tun, ist sie aus den folgenden Gründen fast garantiert langsamer als der oben beschriebene Prozess. Wenn Sie für jede Datei einen neuen Grep-Prozess erzeugen, wird dieser mit Sicherheit langsamer, da der Aufwand für die Prozesserstellung unnötig multipliziert wird. Wenn Sie die Argumentliste selbst im Shell-Skript erstellt und eine einzelne Instanz von verwendet haben grep, ist alles, was Sie in der Shell tun, immer noch langsamer, da Shell-Befehle (per Bash) interpretiert werden müssen, wodurch eine zusätzliche Codeebene hinzugefügt wird Implementieren Sie einfach neu, was bash intern bereits in kompiliertem Code schneller gemacht hat.

Wenn Sie es selbst in C schreiben, können Sie wahrscheinlich leicht eine vergleichbare Leistung wie im ersten Absatz beschrieben erzielen, aber es ist unwahrscheinlich, dass Sie einen ausreichenden Leistungsgewinn gegenüber den aktuellen Grep / Bash-Implementierungen erzielen können, um die Zeit zu rechtfertigen ausgegeben, ohne sich mit maschinenspezifischen Leistungsoptimierungen zu befassen oder die Portabilität zu beeinträchtigen. Vielleicht könnten Sie versuchen, eine willkürlich parallelisierbare Version von zu entwickeln grep, aber selbst das hilft möglicherweise nicht, da Sie eher an E / A als an CPU gebunden sind. Glob Expansion und Grep sind für die meisten "normalen" Zwecke bereits "schnell genug".

jw013
quelle
Danke für die sehr ausführliche Antwort. Eigentlich muss ich komprimierte Dateien (jeweils wenige GB) grep. Ich habe eine Liste dieser Dateien. Ich habe jetzt die Wahl, entweder einen regulären Ausdruck (kompliziert) zu erstellen, der mit diesen Dateien übereinstimmt, oder die bekannte Liste zu durchlaufen und grep für jede dieser Dateien auszuführen (einfach). Daher die Sorge um die Leistung.
Harithski
versuchen Sie zcatund zgrep; keine Notwendigkeit, sie
einzeln
Ja natürlich. Ich benutze zgrep.
Harithski
6

Ja, es wird zu einer Liste von Dateien erweitert und die resultierende Liste dem grepProgramm zugeführt. Zumindest steht das man bashim Unterabschnitt Pfadnamenerweiterung .

Es gibt eine andere Möglichkeit, die Erweiterung in einfachen Fällen zu verwenden, wie Sie bereits erwähnt haben: Schreiben grep <some_string> aund vor dem Drücken* drücken ESC. Dadurch wird die Liste der übereinstimmenden Dateien direkt in der Befehlszeile erweitert, sodass Sie überprüfen können, ob die Liste in Ordnung ist, bevor Sie auf drücken Enter.

Der zweite Teil Ihrer Frage hängt davon ab. Wenn Sie eine for-Schleife schreiben möchten, die nacheinander grep für jede der Dateien ausführt, ist dies definitiv langsamer, da das grep-Programm nicht einmal, sondern einmal pro Datei ausgeführt wird. Doch was ist wichtig im Auge zu behalten ist , dass es eine bestimmte ist Grenze auf der erweiterten Länge der Befehlszeilenargumente können Sie verwenden, obwohl es in der Regel recht hoch ist. Um das zu sehen, können Sie es versuchen grep adasdsadf /usr/*/*/* >/dev/null.

rozcietrzewiacz
quelle
2
ESC+*ist nicht genau das Gleiche wie das Erweitern von bash *, da ESC+*Punktdateien (Namen, die mit a beginnen .) eingefügt werden, während die Erweiterung von *von der dotglob shoptEinstellung abhängt . Die Tastenfolge zum Erweitern und Einfügen von Globs ist C-x *standardmäßig und wird dem Befehl readline zugeordnet glob-expand-word.
jw013
1
@ jw013 Danke für die Information! Es scheint den Fall der a*Expansion nicht zu ändern , ist aber in einem breiteren Bereich sicherlich wichtig.
Rozcietrzewiacz
2
zshHinweis: Durch einfaches Drücken der Tabulatortaste bei erweiterbaren Parametern (Glob-Muster, Klammererweiterung, Befehlssubstitution usw.) werden diese erweitert.
Stéphane Gimenez
@ jw013 Eigentlich habe ich gerade die C-xVerknüpfung getestet und sie erweitert nicht die Liste der Dateien auf meinem System (mit bash).
Rozcietrzewiacz
1
@roz Richtig - ich benutze es sowieso kaum, wollte nur auf den (ziemlich pingeligen) Unterschied hinweisen :). C-x *Nur Globs, die nur Dateinamen verwenden, aber Esc *tatsächlich viel mehr, da dies der Fall ist insert-completions, wie bei allen möglichen Vervollständigungen. Dies bedeutet, dass bei Verwendung Esc *einer leeren Befehlszeile beispielsweise der Name jeder einzelnen ausführbaren Datei in Ihre Datei eingefügt $PATHwird.
jw013