Je dois inspecter tous les sous-répertoires et signaler le nombre de fichiers (sans autre récursivité) qu'ils contiennent:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Je dois inspecter tous les sous-répertoires et signaler le nombre de fichiers (sans autre récursivité) qu'ils contiennent:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Réponses:
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 $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 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 find
commande a simplement -exec echo \;
et non -exec echo {} \;
. Je souhaite uniquement imprimer une nouvelle ligne dans le but de comptabiliser les fichiers.
-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
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
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é pour wc
compter le nombre de ces lignes.
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:
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 -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:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
ou tous les fichiers comme celui-ci:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash a différentes façons de simplifier cela. Pour compter les fichiers normaux:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Pour compter tous les fichiers:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Comme d'habitude, c'est encore plus simple dans zsh. Pour compter les fichiers normaux:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
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
).
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 %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, voir echo -n $'\tfoo\nbar'
vs printf %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.
Voici une façon « force brute » ish pour obtenir votre résultat, en utilisant find
, echo
, ls
, wc
, xargs
et awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
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..
)