Qu'est-ce qui s'étend récursivement à tous les fichiers du répertoire actuel?

91

Je sais qu'il **/*.exts'étend à tous les fichiers de tous les sous-répertoires correspondants *.ext, mais qu'est-ce qu'une extension similaire qui inclut également tous ces fichiers dans le répertoire actuel ?

Ramon
la source
4
Ma fête ne gère pas **/*.ext. Êtes-vous sûr que cela fonctionne pour vous?
tangens
@tangens Vous devez activer l' globstaroption selon la réponse de Dennis.
kenorb

Réponses:

110

Cela fonctionnera dans Bash 4:

ls -l {,**/}*.ext

Pour que le glob double astérisque fonctionne, l' globstaroption doit être définie (par défaut: activé):

shopt -s globstar

De man bash:

    globstar
                  S'il est défini, le modèle ** utilisé dans une extension de nom de fichier
                  le texte correspondra à un fichier et à zéro ou plusieurs répertoires et
                  sous-répertoires. Si le motif est suivi d'un /, seulement
                  les répertoires et les sous-répertoires correspondent.

Maintenant, je me demande s'il peut y avoir eu un bogue dans le traitement de globstar, car maintenant, en utilisant simplement, j'obtiens des ls **/*.extrésultats corrects.

Quoi qu'il en soit, j'ai regardé l' analyse kenorb en utilisant le référentiel VLC et trouvé des problèmes avec cette analyse et dans ma réponse immédiatement ci-dessus:

Les comparaisons avec la sortie de la findcommande ne sont pas valides car la spécification -type fn'inclut pas d'autres types de fichiers (répertoires en particulier) et les lscommandes listées le font probablement. De plus, l'une des commandes répertoriées, ls -1 {,**/}*.*- qui semble être basée sur la mienne ci-dessus, ne renvoie que les noms qui incluent un point pour les fichiers qui se trouvent dans des sous-répertoires. La question de l'OP et ma réponse comportent un point puisque ce qui est recherché, ce sont des fichiers avec une extension spécifique.

Le plus important, cependant, est qu'il y a un problème spécial lors de l'utilisation de la lscommande avec le modèle globstar **. De nombreux doublons surviennent car le modèle est étendu par Bash à tous les noms de fichiers (et noms de répertoires) dans l'arborescence examinée. Suite à l'expansion, la lscommande répertorie chacun d'entre eux et leur contenu s'il s'agit de répertoires.

Exemple:

Dans notre répertoire actuel se trouve le sous-répertoire Aet son contenu:

A
└── AB
    └── ABC
        ├── ABC1
        ├── ABC2
        └── ABCD
            └── ABCD1

Dans cet arbre, se **développe en "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 entrées) . Si vous le faites, echo **c'est la sortie exacte que vous obtiendrez et chaque entrée est représentée une fois. Cependant , si vous le faites, ls **il affichera une liste de chacune de ces entrées. Donc, essentiellement, il est ls Asuivi de ls A/AB, etc., donc il A/ABest montré deux fois. De plus, lsva séparer la sortie de chaque sous-répertoire:

...
<blank line>
directory name:
content-item
content-item

Donc, utiliser wc -lcompte toutes ces lignes vides et ces en-têtes de section de nom de répertoire, ce qui jette le décompte encore plus loin.

C'est une autre raison pour laquelle vous ne devriez pas analyserls .

À la suite de cette analyse plus approfondie, je recommande de ne pas utiliser le modèle globstar dans des circonstances autres que l'itération sur une arborescence de fichiers de cette manière:

for entry in **
do
    something "$entry"
done

En guise de comparaison finale, j'ai utilisé un référentiel de sources Bash que j'avais sous la main et j'ai fait ceci:

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

J'avais l'habitude trde changer les espaces en nouvelles lignes, ce qui n'est valable qu'ici car aucun nom n'inclut d'espace. J'avais l'habitude sedde supprimer le début ./de chaque ligne de sortie find. J'ai trié la sortie findcar elle n'est normalement pas triée et l'expansion des globs de Bash est déjà triée. Comme vous pouvez le voir, la seule sortie de diffétait la .sortie du répertoire courant par find. Quand j'ai fait ls ** | wc -lla sortie avait presque deux fois plus de lignes.

Suspendu jusqu'à nouvel ordre.
la source
5
J'ai testé Ubuntu et Cygwin, et globstarest par défautoff
Steven Penny
12
La meilleure réponse! mais je pense que cela **/*.extdevrait suffire. De plus, vous n'aurez pas les fichiers cachés sauf si vous shopt -s dotglob.
gniourf_gniourf
2
Pour désactiver globstar: shopt -u globstar.
kenorb
4
@gniourf_gniourf La question demande en fait d'inclure le répertoire actuel spécifiquement donc non, **/*.extne suffira pas
msciwoj
2
@dotnetCarpenter: La version de Bash livrée avec MacOS est la 3.2 qui ne prend pas en charge globstar, comme vous l'avez découvert. Un double astérisque est traité de la même manière qu'un simple. Globstar a été introduit dans Bash 4.0.
Suspendu jusqu'à nouvel ordre.
13

Cela affichera tous les fichiers du répertoire courant et de ses sous-répertoires qui se terminent par «.ext».

find . -name '*.ext' -print
unutbu
la source
Bien que cette réponse ne réponde pas à «l'expansion» demandée par le PO au sens strict, elle est plus susceptible de produire le résultat souhaité.
Suspendu jusqu'à nouvel ordre.
7

Vous pouvez utiliser: **/*.*pour inclure tous les fichiers de manière récursive (activer par:) shopt -s globstar.

Veuillez trouver ci-dessous des tests sur d'autres variantes et leur comportement.


Dossier de test avec 3472 fichiers dans l'exemple de dossier du référentiel VLC :

(Fichiers total de 3472 comptés comme par: find . -type f | wc -l)

  • ls -1 **/*.* - renvoie 3338
  • ls -1 {,**/}*.*- renvoie 3341 (comme proposé par Dennis )
  • ls -1 {,**/}* - renvoie 8265
  • ls -1 **/*- renvoie 7817, sauf les fichiers cachés (comme proposé par Dennis )
  • ls -1 **/{.[^.],}*- renvoie 7869 (comme proposé par Dennis )
  • ls -1 {,**/}.?* - renvoie 15855
  • ls -1 {,**/}.* - renvoie 20321

Je pense donc que la méthode la plus proche pour lister tous les fichiers de manière récursive est le premier exemple ( **/*.*) selon le commentaire gniourf-gniourf (en supposant que les fichiers ont les extensions appropriées, ou utilisent celle spécifique), car le deuxième exemple donne quelques doublons supplémentaires comme ci-dessous :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

et l'autre génère encore plus de doublons.


Pour inclure les fichiers cachés, utilisez: shopt -s dotglob(désactiver par shopt -u dotglob). Ce n'est pas recommandé, car cela peut affecter des commandes telles que mvou rmet vous pouvez supprimer accidentellement les mauvais fichiers.

Kenorb
la source
Sur le terminal Mac et bash avec globstar activé, j'ai trouvé la solution ci-dessus ( **/*.*) informative et fonctionnait mieux. La réponse acceptée a provoqué des doublons d'éléments dans le répertoire supérieur. Mon modèle de travail était:"${path}"**/*.*
mummybot
Il serait intéressant d'essayer ceci avec d'autres options comme nullglob et dotglob
Wilf
3
$ find . -type f

Cela listera tous les fichiers du répertoire actuel. Vous pouvez ensuite exécuter une autre commande sur la sortie en utilisant -exec

$find . -type f -exec grep "foo" {} \;

Cela grepera chaque fichier de la recherche pour la chaîne "foo".

Amir Afghani
la source
Maintenant que c'est 11 ans plus tard, il est peut-être temps que quelqu'un souligne que cela find . -type fs'applique de manière récursive avec la racine du répertoire courant, pas seulement au répertoire courant.
Roger Dahl
3

Pourquoi ne pas simplement utiliser l'extension d'accolades pour inclure également le répertoire actuel?

./{*,**/*}.ext

L'expansion de l'accolade se produit avant l'expansion de glob, vous pouvez donc faire ce que vous voulez avec les anciennes versions de bash, et pouvez renoncer au singe avec globstar dans les versions plus récentes.

En outre, il est considéré comme une bonne pratique dans bash d'inclure le leader ./dans vos modèles glob.

clone206
la source