Pourquoi voulez-vous utiliser findquand Bash fera l'affaire? (shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): pour tous les répertoires, comptez le nombre d'entrées dans ce répertoire (y compris les fichiers de points cachés, à l'exclusion de .et ..)
janmoesen
@janmoesen Pourquoi n'avez-vous pas répondu à cela? Je suis nouveau dans les scripts shell, mais je ne vois aucun problème avec votre méthode. Pour moi, cela ressemble à la meilleure façon. Personne n'a voté pour votre commentaire, mais personne n'a commenté pourquoi il pourrait être mauvais non plus. Les réponses votées ont beaucoup plus de représentants que vous, donc je me demande si je manque quelque chose.
toxalot du
@toxalot: Je n'ai pas pris la peine de l'ajouter comme réponse car elle était si courte (et peut-être légèrement condescendante). N'hésitez pas à voter pour le commentaire. :-) En outre, la question est quelque peu vague quant à ce que signifie "combien de fichiers". Ma solution compte les fichiers et répertoires "normaux" ; peut-être que l'affiche voulait vraiment dire "des fichiers, pas des répertoires". Une autre chose à garder à l'esprit est que cette globalisation ne prend pas en compte les fichiers dot "cachés". Il existe cependant des moyens de contourner ces deux pièges. Mais encore une fois: pas sûr des exigences exactes de l'affiche originale.
janmoesen
Réponses:
30
Cela le fait de manière sûre et portable. Il ne sera pas confondu par des noms de fichiers étranges.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Notez qu'il imprimera d'abord le nombre de fichiers, puis le nom du répertoire sur une ligne distincte. Si vous souhaitez conserver le format OP, vous aurez besoin d'un formatage supplémentaire, par exemple
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Si vous avez un ensemble spécifique de sous-répertoires qui vous intéressent, vous pouvez les remplacer *par eux.
Pourquoi est-ce sûr? (et donc digne d'un script)
Les noms de fichiers peuvent contenir n'importe quel caractère sauf /. Il y a quelques caractères qui sont traités spécialement soit par le shell, soit par les commandes. Il s'agit notamment des espaces, des nouvelles lignes et des tirets.
L'utilisation de la for f in *construction est un moyen sûr d'obtenir chaque nom de fichier, quel qu'il soit.
Une fois que vous avez le nom de fichier dans une variable, vous devez toujours éviter des choses comme find $f . S'il $fcontenait le nom de fichier -test, findse plaindrait de l'option que vous venez de lui donner. La façon d'éviter cela est d'utiliser ./en face du nom; de cette façon, il a la même signification, mais il ne commence plus par un tiret.
Les nouvelles lignes et les espaces sont également un problème. S'il est $fcontenu "bonjour, mon pote" comme nom de fichier find ./$f, c'est find ./hello, buddy. Tu disfind de regarder ./hello,et buddy. Si ceux-ci n'existent pas, il se plaindra et ne cherchera jamais ./hello, buddy. C'est facile à éviter - utilisez des guillemets autour de vos variables.
Enfin, les noms de fichiers peuvent contenir des sauts de ligne, donc compter les sauts de ligne dans une liste de noms de fichiers ne fonctionnera pas; vous obtiendrez un compte supplémentaire pour chaque nom de fichier avec une nouvelle ligne. Pour éviter cela, ne comptez pas les sauts de ligne dans une liste de fichiers; comptez plutôt les sauts de ligne (ou tout autre caractère) qui représentent un seul fichier. C'est pourquoi la findcommande a simplement -exec echo \;et non -exec echo {} \;. Je souhaite uniquement imprimer une nouvelle ligne dans le but de comptabiliser les fichiers.
Le décompte inclura le répertoire lui-même. Si vous voulez exclure cela du décompte, utilisez-mindepth 1
toxalot
Vous pouvez également utiliser -printf '\n'au lieu de -exec echo.
toxalot
1
@toxalot vous le pouvez si vous avez une trouvaille qui le supporte -printf, mais pas si vous voulez qu'elle fonctionne sur FreeBSD, par exemple.
Shawn J. Goff
6
En supposant que vous recherchez une solution Linux standard, un moyen relativement simple d'y parvenir est de find:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Où findtraverse les deux sous-répertoires spécifiés, vers un -maxdepthde 1 qui empêche toute récursivité supplémentaire et ne signale que les fichiers ( -type f) séparés par des retours à la ligne. Le résultat est ensuite canalisé pour wccompter le nombre de ces lignes.
J'ai plus de 2 dirs ... Comment puis-je combiner votre commande avec la find . -maxdepth 1 -type dsortie?
ShyBoy
Vous pouvez soit (a) inclure les répertoires requis dans une variable et find $dirs ...ou, (b) s'ils se trouvent exclusivement dans le répertoire d'un niveau supérieur, glob de ce répertoire,find */ ...
jasonwryan
1
Cela signalera des résultats incorrects si un nom de fichier contient un caractère de nouvelle ligne.
Shawn J. Goff
@ Shawn: merci. Je pensais que j'avais des noms de fichiers avec des espaces couverts, mais je n'avais pas envisagé de nouvelles lignes: des suggestions de correction?
jasonwryan
Ajoutez -exec echoà votre commande find - de cette façon, elle ne fait pas écho au nom de fichier, juste une nouvelle ligne.
Shawn J. Goff
5
Par «sans récursivité», voulez-vous dire que si directoryName1 a des sous-répertoires, alors vous ne voulez pas compter les fichiers dans les sous-répertoires? Si oui, voici un moyen de compter tous les fichiers normaux dans les répertoires indiqués:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
Notez que le -ftest remplit deux fonctions: il teste si l'entrée correspondant à l'un des globs ci-dessus est un fichier normal, et il teste si l'entrée était une correspondance (si l'un des globs ne correspond à rien, le modèle reste tel quel¹). Si vous souhaitez compter toutes les entrées dans les répertoires donnés quel que soit leur type, remplacez-f par -e.
Ksh a un moyen de faire correspondre les motifs aux fichiers de points et de produire une liste vide au cas où aucun fichier ne correspondrait à un motif. Donc, dans ksh, vous pouvez compter des fichiers réguliers comme celui-ci:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
¹ Notez que chaque modèle correspond lui-même, sinon les résultats peuvent être désactivés (par exemple, si vous comptez des fichiers qui commencent par un chiffre, vous ne pouvez pas simplement le faire for x in [0-9]*; do if [ -f "$x" ]; then …car il peut y avoir un fichier appelé [0-9]foo).
Basé sur un script de comptage , la réponse de Shawn et une astuce Bash pour s'assurer que même les noms de fichiers avec des retours à la ligne sont imprimés sous une forme utilisable sur une seule ligne:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %qconsiste à imprimer une version entre guillemets d'une chaîne, c'est-à-dire une chaîne à une seule ligne que vous pourriez mettre dans un script Bash pour être interprétée comme une chaîne littérale comprenant (potentiellement) des retours à la ligne et d'autres caractères spéciaux. Par exemple, voir echo -n $'\tfoo\nbar'vs printf %q $'\tfoo\nbar'.
La findcommande fonctionne en imprimant simplement un seul caractère pour chaque fichier, puis en les comptant au lieu de compter les lignes.
Bienvenue chez U&L. Les réponses doivent être longues avec des explications et pas simplement des baisses de code. Veuillez développer ceci et expliquer ce qui se passe. C'est également une façon très inefficace de le faire et ne gère pas les fichiers avec des espaces, par exemple.
slm
-1
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
quand Bash fera l'affaire?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: pour tous les répertoires, comptez le nombre d'entrées dans ce répertoire (y compris les fichiers de points cachés, à l'exclusion de.
et..
)Réponses:
Cela le fait de manière sûre et portable. Il ne sera pas confondu par des noms de fichiers étranges.
Notez qu'il imprimera d'abord le nombre de fichiers, puis le nom du répertoire sur une ligne distincte. Si vous souhaitez conserver le format OP, vous aurez besoin d'un formatage supplémentaire, par exemple
Si vous avez un ensemble spécifique de sous-répertoires qui vous intéressent, vous pouvez les remplacer
*
par eux.Pourquoi est-ce sûr? (et donc digne d'un script)
Les noms de fichiers peuvent contenir n'importe quel caractère sauf
/
. Il y a quelques caractères qui sont traités spécialement soit par le shell, soit par les commandes. Il s'agit notamment des espaces, des nouvelles lignes et des tirets.L'utilisation de la
for f in *
construction est un moyen sûr d'obtenir chaque nom de fichier, quel qu'il soit.Une fois que vous avez le nom de fichier dans une variable, vous devez toujours éviter des choses comme
find $f
. S'il$f
contenait le nom de fichier-test
,find
se plaindrait de l'option que vous venez de lui donner. La façon d'éviter cela est d'utiliser./
en face du nom; de cette façon, il a la même signification, mais il ne commence plus par un tiret.Les nouvelles lignes et les espaces sont également un problème. S'il est
$f
contenu "bonjour, mon pote" comme nom de fichierfind ./$f
, c'estfind ./hello, buddy
. Tu disfind
de regarder./hello,
etbuddy
. Si ceux-ci n'existent pas, il se plaindra et ne cherchera jamais./hello, buddy
. C'est facile à éviter - utilisez des guillemets autour de vos variables.Enfin, les noms de fichiers peuvent contenir des sauts de ligne, donc compter les sauts de ligne dans une liste de noms de fichiers ne fonctionnera pas; vous obtiendrez un compte supplémentaire pour chaque nom de fichier avec une nouvelle ligne. Pour éviter cela, ne comptez pas les sauts de ligne dans une liste de fichiers; comptez plutôt les sauts de ligne (ou tout autre caractère) qui représentent un seul fichier. C'est pourquoi la
find
commande a simplement-exec echo \;
et non-exec echo {} \;
. Je souhaite uniquement imprimer une nouvelle ligne dans le but de comptabiliser les fichiers.la source
-mindepth 1
-printf '\n'
au lieu de-exec echo
.-printf
, mais pas si vous voulez qu'elle fonctionne sur FreeBSD, par exemple.En supposant que vous recherchez une solution Linux standard, un moyen relativement simple d'y parvenir est de
find
:Où
find
traverse les deux sous-répertoires spécifiés, vers un-maxdepth
de 1 qui empêche toute récursivité supplémentaire et ne signale que les fichiers (-type f
) séparés par des retours à la ligne. Le résultat est ensuite canalisé pourwc
compter le nombre de ces lignes.la source
find . -maxdepth 1 -type d
sortie?find $dirs ...
ou, (b) s'ils se trouvent exclusivement dans le répertoire d'un niveau supérieur, glob de ce répertoire,find */ ...
-exec echo
à votre commande find - de cette façon, elle ne fait pas écho au nom de fichier, juste une nouvelle ligne.Par «sans récursivité», voulez-vous dire que si
directoryName1
a des sous-répertoires, alors vous ne voulez pas compter les fichiers dans les sous-répertoires? Si oui, voici un moyen de compter tous les fichiers normaux dans les répertoires indiqués:Notez que le
-f
test remplit deux fonctions: il teste si l'entrée correspondant à l'un des globs ci-dessus est un fichier normal, et il teste si l'entrée était une correspondance (si l'un des globs ne correspond à rien, le modèle reste tel quel¹). Si vous souhaitez compter toutes les entrées dans les répertoires donnés quel que soit leur type, remplacez-f
par-e
.Ksh a un moyen de faire correspondre les motifs aux fichiers de points et de produire une liste vide au cas où aucun fichier ne correspondrait à un motif. Donc, dans ksh, vous pouvez compter des fichiers réguliers comme celui-ci:
ou tous les fichiers comme celui-ci:
Bash a différentes façons de simplifier cela. Pour compter les fichiers normaux:
Pour compter tous les fichiers:
Comme d'habitude, c'est encore plus simple dans zsh. Pour compter les fichiers normaux:
Changez
(DN.)
pour(DN)
compter tous les fichiers.¹ Notez que chaque modèle correspond lui-même, sinon les résultats peuvent être désactivés (par exemple, si vous comptez des fichiers qui commencent par un chiffre, vous ne pouvez pas simplement le faire
for x in [0-9]*; do if [ -f "$x" ]; then …
car il peut y avoir un fichier appelé[0-9]foo
).la source
Basé sur un script de comptage , la réponse de Shawn et une astuce Bash pour s'assurer que même les noms de fichiers avec des retours à la ligne sont imprimés sous une forme utilisable sur une seule ligne:
printf %q
consiste à imprimer une version entre guillemets d'une chaîne, c'est-à-dire une chaîne à une seule ligne que vous pourriez mettre dans un script Bash pour être interprétée comme une chaîne littérale comprenant (potentiellement) des retours à la ligne et d'autres caractères spéciaux. Par exemple, voirecho -n $'\tfoo\nbar'
vsprintf %q $'\tfoo\nbar'
.La
find
commande fonctionne en imprimant simplement un seul caractère pour chaque fichier, puis en les comptant au lieu de compter les lignes.la source
Voici une façon « force brute » ish pour obtenir votre résultat, en utilisant
find
,echo
,ls
,wc
,xargs
etawk
.la source
la source
la source