web-dev-qa-db-fra.com

Qu'est-ce qui se développe récursivement à tous les fichiers du répertoire actuel?

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

86
Ramon

Cela fonctionnera dans Bash 4:

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

Pour que le glob à double astérisque fonctionne, l'option globstar doit être définie (par défaut: on):

shopt -s globstar

De man bash:

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

Maintenant, je me demande s'il y a déjà eu un bogue dans le traitement de globstar, car maintenant j'utilise simplement ls **/*.ext J'obtiens des résultats corrects.

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

Les comparaisons avec la sortie de la commande find ne sont pas valides car la spécification de -type f N'inclut pas les autres types de fichiers (répertoires en particulier) et les commandes ls répertoriées le font probablement. En outre, l'une des commandes répertoriées, ls -1 {,**/}*.* - qui semble être basée sur la mienne ci-dessus, ne produit que les noms qui incluent un point pour les fichiers qui se trouvent dans les sous-répertoires. La question du PO et ma réponse incluent un point, car ce qui est recherché, ce sont des fichiers avec une extension spécifique.

Le plus important, cependant, est qu'il existe un problème spécial à l'aide de la commande ls avec le modèle globstar **. De nombreux doublons apparaissent 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 liste de commandes ls chaque et leur contenu s'ils sont des répertoires .

Exemple:

Dans notre répertoire actuel se trouve le sous-répertoire A et 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 faites echo **, C'est la sortie exacte que vous obtiendrez et chaque entrée est représentée une fois. Cependant, si vous faites ls **, Il va afficher une liste de chacun de ces entrées. Donc, essentiellement, il fait ls A Suivi de ls A/AB, Etc., donc A/AB S'affiche deux fois. De plus, ls va mettre à part la sortie de chaque sous-répertoire:

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

Ainsi, l'utilisation de wc -l Compte toutes ces lignes vides et les en-têtes de section de nom de répertoire, ce qui jette encore plus loin le décompte.

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

À la suite de cette analyse plus approfondie, je recommande de ne pas utiliser le modèle globstar dans aucune circonstance autre que l'itération sur un arbre de fichiers de cette manière:

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

Comme comparaison finale, j'ai utilisé un référentiel source Bash que j'avais à portée de main et j'ai fait ceci:

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

J'ai utilisé tr pour remplacer les espaces par des sauts de ligne qui ne sont valables que dans la mesure où aucun nom ne contient d'espaces. J'ai utilisé sed pour supprimer le premier ./ De chaque ligne de sortie de find. J'ai trié la sortie de find car elle n'est normalement pas triée et l'expansion de globs par Bash est déjà triée. Comme vous pouvez le voir, la seule sortie de diff était le répertoire courant . Sorti par find. Quand j'ai fait ls ** | wc -l La sortie avait presque deux fois plus de lignes.

102

Cela imprimera tous les fichiers du répertoire courant et ses sous-répertoires qui se terminent par '.ext'.

find . -name '*.ext' -print
12
unutbu

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 VLC dossier du référentiel:

(Total de 3472 fichiers comptés selon: 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 répertorier tous les fichiers de manière récursive est le premier exemple (**/*.*) selon gniourf-gniourf comment (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 des doublons encore plus.


Pour inclure des 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 mv ou rm et vous pouvez supprimer accidentellement les mauvais fichiers.

6
kenorb

Pourquoi ne pas simplement utiliser l'extension d'accolade 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 vous pouvez renoncer au monkey avec globstar dans les versions plus récentes.

En outre, il est considéré comme une bonne pratique en bash d'inclure le premier ./ dans vos motifs globaux.

3
clone206
$ find . -type f

Cela listera tous les fichiers du répertoire courant. Vous pouvez ensuite exécuter une autre commande sur la sortie à l'aide de -exec

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

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

3
Amir Afghani