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?
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.
Cela imprimera tous les fichiers du répertoire courant et ses sous-répertoires qui se terminent par '.ext'.
find . -name '*.ext' -print
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 8265ls -1 **/*
- renvoie 7817, sauf les fichiers cachés (comme proposé par Dennis )ls -1 **/{.[^.],}*
- renvoie 7869 (comme proposé par Dennis )ls -1 {,**/}.?*
- renvoie 15855ls -1 {,**/}.*
- renvoie 20321Je 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.
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.
$ 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".