Glob-Dateien * ohne * Verwendung einer Shell

Aug 22 2020

Ich möchte Dateien in einem bestimmten Unterverzeichnis auflisten, aber ich mache dies als Teil docker execeines Docker-Containers, damit ich nicht die Mühe habe, eine Shell zu starten, die ich nicht wirklich brauche. Ist es möglich, alle Übereinstimmungen für einen Glob mit einem einfachen Befehlszeilen-Tool zu finden und nicht nur mit einer Shell?

Zum Beispiel ist mein aktueller Aufruf bash -l -c 'echo /usr/local/conda-meta/*.json'. Ist es möglich, dies mit einem allgemein verfügbaren Werkzeug zu vereinfachen, was zu etwas führt globber /usr/local/conda-meta/*.json, das viel einfacher und leichter wäre?

Antworten

16 StéphaneChazelas Aug 23 2020 at 05:12

shist einfach und allgemein verfügbar. shist das Tool, das aufgerufen wird, um Befehlszeilen in Dingen wie system(cmdline)in vielen Sprachen zu analysieren . Viele Betriebssysteme, einschließlich einiger GNU-Betriebssysteme, verwenden bashdie GNU-Shell nicht mehr (die GNU-Shell), um sie zu implementieren sh, da sie zu aufgebläht ist, um nur das einfache Parsen von Befehlszeilen und das Interpretieren von POSIX- shSkripten durchzuführen .

Ihre bash -l -c 'echo /usr/local/conda-meta/*.json'Befehlszeile wird möglicherweise bereits von einem shAufruf interpretiert . Möglicherweise können Sie also einfach Folgendes tun:

printf '%s\n' /usr/local/conda-meta/*.json

direkt. Wenn nicht:

sh -c 'printf "%s\n" /usr/local/conda-meta/*.json'

Sie können auch findhier verwenden. findmacht kein Globbing, kann aber Dateinamen melden, die mit Mustern übereinstimmen, die denen der Shell ähneln.

LC_ALL=C find /usr/local/conda-meta/. ! -name . -prune -name '*.json'

Oder mit einigen findImplementierungen:

LC_ALL=C find /usr/local/conda-meta -mindepth 1 -maxdepth 1 -name '*.json'

(Beachten Sie, dass das LC_ALL=Chier erforderliche Element, das *mit einer beliebigen Folge von Bytes übereinstimmt, nicht nur mit denen, die im aktuellen Gebietsschema gültige Zeichen bilden, ein Shell-Konstrukt ist. Wenn diese Befehlszeile nicht von einer Shell interpretiert wird, müssen Sie sie möglicherweise ändern env LC_ALL=C find...)

Einige Unterschiede zu Shell Globs:

  • Die Liste der Dateien ist nicht sortiert
  • versteckte Dateien sind enthalten (Sie können eine hinzufügen ! -name '.*', um sie auszuschließen)
  • Sie erhalten keine Ausgabe, wenn keine passende Datei vorhanden ist. Globs haben die Fehlfunktion, dass sie das Muster so lassen, wie es in diesem Fall nicht erweitert ist.
  • Bei der ersten (Standard-) Variante werden Dateien als ausgegeben /usr/local/conda-meta/./file.json.
  • Einige Globs wie x*/y/../*zsind nicht einfach zu übersetzen (beachten Sie auch das unterschiedliche Verhalten in Bezug auf Symlinks zu Verzeichnissen in diesem Fall).

In jedem Fall können Sie keine echobeliebigen Daten ausgeben.

Meine nächste Frage wäre: Was machen Sie mit dieser Ausgabe? Mit geben echoSie diese Dateipfade aus, die durch SPC-Zeichen getrennt und mit my printfoder findhöher durch NL-Zeichen getrennt sind. Beide NLund SPCsind vollkommen gültige Zeichen in Dateinamen, sodass diese Ausgaben nicht zuverlässig nachbearbeitbar sind. Sie können '%s\0'anstelle von '%s\n'(oder find, -print0falls unterstützt, 'verwenden , nicht für die Anzeige für einen Benutzer geeignet, aber nachbearbeitbar.

In Bezug auf die Effizienz wird Ubuntu /bin/sh20.04 (Strich 0.5.10.2) mit seinem find(GNU find4.7.0) verglichen.

Startzeit:

$ time (repeat 1000 sh -c '') ( repeat 1000; do; sh -c ''; done; ) 0.91s user 0.66s system 105% cpu 1.483 total $ time (repeat 1000 find . -quit)
( repeat 1000; do; find . -quit; done; )  1.35s user 1.25s system 103% cpu 2.507 total

Globbing einiger jsonDateien:

$ TIMEFMT='%U user %S system %P cpu %*E total' $ time (repeat 1000 sh -c 'printf "%s\n" /usr/share/iso-codes/json/*.json') > /dev/null
0.95s user 0.72s system 105% cpu 1.587 total
$ time (repeat 1000  find /usr/share/iso-codes/json -mindepth 1 -maxdepth 1 -name '*.json') > /dev/null
1.34s user 1.35s system 103% cpu 2.599 total

Auch bashist kaum langsamer als findhier:

$ time (repeat 1000 bash -c 'printf "%s\n" /usr/share/iso-codes/json/*.json') > /dev/null
1.53s user 1.36s system 102% cpu 2.808 total

Natürlich YMMV abhängig vom System, der Implementierung, der Version der jeweiligen Dienstprogramme und den Bibliotheken, mit denen sie verknüpft sind.

In der Verlaufsnotiz stammt der Glob- Name tatsächlich vom Namen eines Dienstprogramms, das globin den ersten Unix-Versionen in den frühen 70er Jahren aufgerufen wurde . Es befand sich in /etcund wurde von shals Helfer aufgerufen , um Platzhaltermuster zu erweitern.

Sie finden einige Projekte online, um diese sehr alte Shell wiederzubeleben, wie z https://etsh.nl/. Mehr als eine Übung in Archäologie, könnten Sie das globDienstprogramm von dort aus aufbauen und dann in der Lage sein:

glob printf '%s\n' '/usr/local/conda-meta/*.json'

Ein paar Warnhinweise.

  • Das sind alte Klumpen [!x](geschweige denn [^x]), die nicht unterstützt werden.
  • Es ist nicht 8 Bit sicher. Tatsächlich wird das 8. Bit verwendet, um den Glob-Operatoren zu entkommen ( $'\xe9*'würde mit dem übereinstimmen i*, $'\xaa*'was mit Dateinamen übereinstimmt, die mit beginnen *; die Shell würde das 8. Bit für die zitierten Zeichen vor dem Aufrufen setzen glob).
  • Bereiche wie [a-f]Übereinstimmung auf Byte-Wert statt Sortierreihenfolge (in der Praxis ist dies im Allgemeinen ein Vorteil, IMO).
  • Nicht übereinstimmende Globs führen zu einem No matchFehler (auch dies ist wahrscheinlich vorzugsweise etwas, das Ende der 70er Jahre von der Bourne-Shell gebrochen wurde).

Die globFunktionalität wurde später in die Shell verschoben, beginnend mit der PWB-Shell und der Bourne-Shell Ende der 70er Jahre. Später wurden einige fnmatch()und glob()Funktionen zur C-Bibliothek hinzugefügt, damit diese Funktion von anderen Anwendungen aus verwendet werden kann. Mir ist jedoch weder ein Standard noch ein allgemeines Dienstprogramm bekannt, das eine bloße Schnittstelle zu dieser Funktion darstellt. Sogar perlverwendet, um cshin seinen frühen Tagen aufzurufen , um Glob-Muster zu erweitern.

7 BasileStarynkevitch Aug 23 2020 at 14:50

Glob-Dateien ohne Shell

Die naheliegende Dokumentation ist glob (7) .

Sie können ein C-Programm schreiben oder verwenden, das fnmatch (3) , glob (3) , nftw (3) , stat (2) , readdir (3) aufruft.

Wenn Sie in Guile , Python , Go , Rust , Ocaml , Common Lisp (z. B. SBCL ) codieren , finden Sie ähnliche Funktionen. Mit C ++ schauen Sie in POCO und Qt .

Ich gehe davon aus, dass Sie ein Linux-System verwenden. Übrigens ist meine interaktive Shell zsh (deren Autocompletion-Funktionen meiner Meinung nach vorzuziehen sind).