Fichiers Glob * sans * utiliser un shell
Je veux lister les fichiers dans un certain sous-répertoire, mais je le fais dans le cadre d'un docker execconteneur à l'intérieur d'un docker, donc je ne veux pas prendre la peine de démarrer un shell dont je n'ai pas vraiment besoin. Est-il possible de trouver toutes les correspondances pour un glob avec un simple outil de ligne de commande, et pas seulement un shell?
Par exemple, mon appel actuel est bash -l -c 'echo /usr/local/conda-meta/*.json'. Est-il possible de simplifier cela en utilisant un outil couramment disponible, ce qui donne quelque chose comme globber /usr/local/conda-meta/*.json, qui serait beaucoup plus simple et plus léger?
Réponses
shest simple et couramment disponible. shest l'outil qui est appelé pour analyser les lignes de commande dans des choses comme system(cmdline)dans de nombreuses langues. De nombreux systèmes d'exploitation, y compris certains GNU, ont cessé d'utiliser bash(le shell GNU) pour l'implémentation shparce qu'il est devenu trop lourd pour faire juste cette chose simple d'analyse des lignes de commande et d'interprétation des shscripts POSIX .
Votre bash -l -c 'echo /usr/local/conda-meta/*.json'ligne de commande est peut-être déjà interprétée par un shappel. Alors peut-être que vous pouvez simplement faire:
printf '%s\n' /usr/local/conda-meta/*.json
directement. Si non:
sh -c 'printf "%s\n" /usr/local/conda-meta/*.json'
Vous pouvez également utiliser findici. findne fait pas de globulation mais il peut rapporter des noms de fichiers qui correspondent à des modèles similaires à ceux du shell.
LC_ALL=C find /usr/local/conda-meta/. ! -name . -prune -name '*.json'
Ou avec quelques findimplémentations:
LC_ALL=C find /usr/local/conda-meta -mindepth 1 -maxdepth 1 -name '*.json'
(notez que le LC_ALL=Cnécessaire ici pour *correspondre à n'importe quelle séquence d'octets, et pas seulement à ceux qui forment des caractères valides dans la locale actuelle, est une construction shell. Si cette ligne de commande n'est pas interprétée par un shell, vous devrez peut-être la changer en env LC_ALL=C find...)
Quelques différences avec les globes shell:
- la liste des fichiers n'est pas triée
- les fichiers cachés sont inclus (vous pouvez ajouter un
! -name '.*'pour les exclure) - vous n'obtenez aucune sortie s'il n'y a pas de fichier correspondant. les globes ont cette anomalie qu'ils laissent le modèle tel quel non développé dans ce cas.
- avec la première variante (standard), les fichiers seront générés au format
/usr/local/conda-meta/./file.json. - certains globs tels que
x*/y/../*zne sont pas facilement traduits (notez également le comportement différent en ce qui concerne les liens symboliques vers les répertoires dans ce cas).
Dans tous les cas, vous ne pouvez pas utiliser echopour générer des données arbitraires.
Ma prochaine question serait: qu'allez-vous faire avec ce résultat? Avec echo, vous sortez ces chemins de fichiers séparés par des caractères SPC et avec mon printfou findau-dessus, délimités par des caractères NL. Les deux NLet SPCsont des caractères parfaitement valides dans les noms de fichiers, donc ces sorties ne sont pas fiables post-processables. Vous pouvez utiliser au '%s\0'lieu de '%s\n'(ou utiliser find« s -print0si pris en charge), ne convient pas pour l' affichage à un utilisateur, mais après traitable.
En termes d'efficacité, comparer Ubuntu /bin/sh20.04 (tiret 0.5.10.2) avec son find(GNU find4.7.0).
Temps de démarrage:
$ 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 certains jsonfichiers:
$ 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
Même bashn'est guère plus lent findqu'ici:
$ 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
Bien sûr, YMMV dépend du système, de l'implémentation, de la version des utilitaires respectifs et des bibliothèques auxquelles ils sont liés.
Maintenant sur la note historique, le nom glob vient en fait du nom d'un utilitaire appelé globdans les toutes premières versions d'Unix au début des années 70. Il a été localisé dans /etcet a été invoqué par en shtant qu'assistant pour développer les modèles de caractères génériques.
Vous trouverez quelques projets en ligne pour faire revivre ce très vieux shell tel que https://etsh.nl/. Plus comme un exercice d'archéologie, vous pourriez construire l' globutilitaire à partir de là et être capable de faire:
glob printf '%s\n' '/usr/local/conda-meta/*.json'
Quelques notes d'avertissement cependant.
- ce sont des globes anciens,
[!x](encore moins[^x]) n'est pas pris en charge. - ce n'est pas sûr 8 bits. En fait, le 8ème bit est utilisé pour échapper les opérateurs glob (
$'\xe9*'correspondrait à la même chose quei*,$'\xaa*'correspondrait aux noms de fichiers commençant par*; le shell définirait ce 8ème bit pour les caractères entre guillemets avant d'appelerglob) - des plages comme la
[a-f]correspondance sur la valeur d'octet plutôt que l'ordre de classement (en pratique, c'est généralement un avantage IMO). - Les globes non correspondants entraînent une
No matcherreur (encore une fois, probablement de préférence, c'est quelque chose qui a été cassé par le shell Bourne à la fin des années 70).
La globfonctionnalité a ensuite été déplacée dans le shell en commençant par le shell PWB et le shell Bourne à la fin des années 70. Plus tard, certaines fonctions fnmatch()et glob()ont été ajoutées à la bibliothèque C pour permettre à cette fonctionnalité d'être utilisée à partir d'autres applications, mais je ne suis pas au courant d'un utilitaire standard ou commun qui est une interface nue à cette fonction. Même perlutilisé pour invoquer cshà ses débuts pour élargir les modèles globaux.
Fichiers Glob sans utiliser de shell
La documentation évidente à lire est glob (7) .
Vous pouvez écrire ou utiliser un programme C appelant fnmatch (3) , glob (3) , nftw (3) , stat (2) , readdir (3)
Si vous codez en Guile , Python , Go , Rust , Ocaml , Common Lisp (par exemple SBCL ) ... vous trouverez des fonctions similaires. Avec C ++, regardez dans POCO et Qt .
Je suppose que vous utilisez un système Linux. BTW, mon shell interactif est zsh (dont les installations de saisie semi-automatique sont préférables à mon humble avis).