Comment compter le nombre de fichiers dans chaque répertoire?


105

Je peux lister tous les répertoires par

find ./ -type d

J'ai essayé de répertorier le contenu de chaque répertoire et de compter le nombre de fichiers dans chaque répertoire à l'aide de la commande suivante

find ./ -type d | xargs ls -l | wc -l

Mais cela a additionné le nombre total de lignes renvoyées par

find ./ -type d | xargs ls -l

Existe-t-il un moyen de compter le nombre de fichiers dans chaque répertoire?


Cherchez-vous un moyen de compter le nombre de fichiers dans chacun des sous-répertoires directement sous ./?
Tuxdude

5
En quoi est-ce une question hors sujet? J'aimerais voir les commentaires des électeurs proches avec raison! Si c'est hors sujet, à quoi cela appartient-il? super utilisateur? Je ne pense pas ...
InfantPro'Aravind '

6
shell-script, batch-script sont sous la portée de la programmation!
InfantPro'Aravind '

J'étais sur le point de poster une solution pythonique puis j'ai remarqué que la question était close.
anatoly techtonik

voté pour le rouvrir. Il peut y avoir d'autres réponses qui pourraient être utiles dans de nombreuses situations (y compris la programmation de scripts, qui est la raison pour laquelle j'ai atteint cette question).
lepe

Réponses:


110

En supposant que vous avez GNU find, laissez-le trouver les répertoires et laissez bash faire le reste:

find . -type d -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

2
C'est juste une version légèrement différente de celle ci-dessus, donc: (indice: son trié par nom et son en csv) pour x in find . -maxdepth 1 -type d | sort; faire y = find $x | wc -l; echo $ x, $ y; done
pcarvalho

5
Elle est bonne! Mettre en une seule ligne (donc c'est confortable pour une utilisation directe dans le shell):find . -type d -print0 | while read -d '' -r dir; do files=("$dir"/*); printf "%5d files in directory %s\n" "${#files[@]}" "$dir"; done
lucaferrario

13
J'avais besoin d'obtenir le nombre de tous les fichiers (compte récursivement) dans chaque sous-répertoire. Cette modification vous donne que: find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find $dir -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
OmidS

1
@Kory Ce qui suit va le faire:find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done | sort -rn -k1
OmidS

1
@OmidS Excellent oneliner, mais $dirdevrait être entre guillemets dans votre premier commentaire pour gérer correctement les noms de répertoires avec des espaces. :find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
Radek Daniluk

183

Ceci imprime le nombre de fichiers par répertoire pour le niveau de répertoire actuel:

du -a | cut -d/ -f2 | sort | uniq -c | sort -nr

9
De loin la meilleure (et la plus élégante) solution si l'on veut lister récursivement le nombre de fichiers dans les répertoires de premier niveau.
itoctopus

13
Cela pose deux problèmes: cela compte un fichier par répertoire de plus qu'il n'y en a réellement et cela donne une ligne inutile contenant la taille du répertoire courant comme "1 size ". Les deux peuvent être fixés avec du -a | sed '/.*\.\/.*\/.*/!d' | cut -d/ -f2 | sort | uniq -c. Ajouter | sort -nrpour trier par le nombre au lieu du nom du répertoire.
dessert

3
Je tiens à souligner que cela fonctionne également sous OSX. (Le simple copier-coller des conseils Linux dans un shell OSX ne fonctionne généralement pas.)
Pistos

2
il récupère la taille inutile par du -a. Le meilleur moyen consiste à utiliser la commande find. mais l'idée principale est exactement la même :)
Znik

5
trouver . -type f | couper -d / -f2 | trier | uniq -c | sort -nr # corrige les problèmes mentionnés par dessert
jcomeau_ictx

28
find . -type f | cut -d/ -f2 | sort | uniq -c
  • find. -type f pour trouver tous les éléments du fichier type
  • cut -d/ -f2 pour découper leur dossier spécifique
  • sort pour trier la liste des noms de dossiers
  • uniq -c pour renvoyer le nombre de fois que chaque nom de dossier a été compté

8
C'est tellement mieux que la réponse acceptée, car vous obtenez un résumé des répertoires de premier niveau!
Jason Floyd

3
Cela devrait être la réponse acceptée. Simple et compréhensible.
xssChauhan

1
La meilleure réponse qui devrait être acceptée est celle-ci.
loretoparisi

1
Simple, élégant et parfait pour mes besoins.
RichR

Parfait. Et peut être étendu pour compter sur les sous-répertoires en remplaçant les spécificateurs de champ par une liste de spécificateurs de champ. Par exemple:find . -type f | cut -d/ -f2,3 | sort | uniq -c
algal le

15

Vous pouvez vous arranger pour trouver tous les fichiers, supprimer les noms de fichiers, vous laissant une ligne contenant uniquement le nom de répertoire pour chaque fichier, puis compter le nombre de fois que chaque répertoire apparaît:

find . -type f |
sed 's%/[^/]*$%%' |
sort |
uniq -c

Le seul problème est que vous avez des noms de fichiers ou de répertoires contenant un caractère de nouvelle ligne, ce qui est assez improbable. Si vous avez vraiment à vous soucier des nouvelles lignes dans les noms de fichiers ou de répertoires, je vous suggère de les trouver et de les corriger afin qu'elles ne contiennent pas de nouvelles lignes (et persuader tranquillement le coupable de l'erreur de ses chemins).


Si vous êtes intéressé par le nombre de fichiers dans chaque sous-répertoire du répertoire actuel, en comptant tous les fichiers dans tous les sous-répertoires avec les fichiers dans le sous-répertoire immédiat, alors j'adapterais la sedcommande pour imprimer uniquement le répertoire de niveau supérieur:

find . -type f |
sed -e 's%^\(\./[^/]*/\).*$%\1%' -e 's%^\.\/[^/]*$%./%' |
sort |
uniq -c

Le premier motif capture le début du nom, le point, la barre oblique, le nom jusqu'à la barre oblique suivante et la barre oblique, et remplace la ligne par juste la première partie, donc:

./dir1/dir2/file1

est remplacé par

./dir1/

Le deuxième remplacement capture les fichiers directement dans le répertoire courant; ils n'ont pas de barre oblique à la fin, et ceux-ci sont remplacés par ./. Le tri et le décompte fonctionnent alors uniquement sur le nombre de noms.


1
Cela ne génère pas les noms de répertoires qui ne contiennent aucun fichier. Je ne sais pas si cela est nécessaire.
Austin Phillips

C'est vrai que non. Ce n'est pas particulièrement simple de résoudre ce problème, car les noms de répertoire vides ne sont pas garantis même d'apparaître dans la sortie de find. Certains pourraient: s'il y a un fichier dir1/dir2/dir3/file1, mais dir1/dir2ne contient que des sous-répertoires (pas de fichiers simples), alors vous pouvez en déduire sa présence. Mais s'il dir1/dir4n'a pas de fichiers, son nom n'apparaît tout simplement pas.
Jonathan Leffler

Réponse très utile si vous voulez juste voir les sous-répertoires du répertoire courant.
xixixao

Je viens juste de dire merci. 3 ans après sa publication, je cherchais à compter les dossiers de 2e niveau par dossier. Votre message m'a sauvé potentiellement de nombreuses heures à bricoler avec sed, trouver et qui sait quoi d'autre
Corvin

13

Voici une façon de le faire, mais probablement pas la plus efficace.

find -type d -print0 | xargs -0 -n1 bash -c 'echo -n "$1:"; ls -1 "$1" | wc -l' --

Donne une sortie comme celle-ci, avec le nom du répertoire suivi du nombre d'entrées dans ce répertoire. Notez que le nombre de sorties comprendra également les entrées de répertoire qui peuvent ne pas être ce que vous souhaitez.

./c/fa/l:0
./a:4
./a/c:0
./a/a:1
./a/a/b:0

Il semble très coûteux pour exécuter des commandes 3 ( bash, ls, wc) pour chaque répertoire trouvé par find.
Jonathan Leffler

@JonathanLeffler D'accord, d'où la première ligne de ma réponse. Votre solution est meilleure.
Austin Phillips

cool c'est ce que je recherche puis-je demander quel est le «-» à la fin?
une fois

1
@once Le - appartient à la commande bash qui sera générée par xargs. De man bash, A -- signals the end of options and disables further option processing. Dans ce cas, cela empêcherait qu'un fichier mal nommé trouvé dans le cadre de la recherche ne fasse partie du traitement des arguments pour bash.
Austin Phillips

8

La solution de tout le monde a un inconvénient ou un autre.

find -type d -readable -exec sh -c 'printf "%s " "$1"; ls -1UA "$1" | wc -l' sh {} ';'

Explication:

  • -type d: nous nous intéressons aux répertoires.
  • -readable: Nous ne les voulons que s'il est possible de lister les fichiers qu'ils contiennent. Notez que findcela émettra toujours une erreur lorsqu'il essaiera de rechercher plus de répertoires dans ces répertoires, mais cela empêche de les appeler -exec.
  • -exec sh -c BLAH sh {} ';': pour chaque répertoire, exécutez ce fragment de script, avec $0défini sur shet $1défini sur le nom de fichier.
  • printf "%s " "$1": affiche de manière portative et minimale le nom du répertoire, suivi uniquement d'un espace, pas d'une nouvelle ligne.
  • ls -1UA: lister les fichiers, un par ligne, dans l'ordre des répertoires (pour éviter de caler le tube), en excluant uniquement les répertoires spéciaux .et..
  • wc -l: compter les lignes

1
Modification pour afficher le fichier compte en premier sur la ligne, et pour trier par eux:find -type d -readable -exec sh -c 'ls -1UA "$1" | wc -l | tr -d "\n" ; printf "\t%s\n" "$1" ' sh {} ';' | sort -n
Evgeni Sergeev

il exécute le shell plusieurs fois, puis il est lent et utilise fortement les ressources.
Znik

6

Version légèrement modifiée de la réponse de Sebastian utilisant findau lieu de du(pour exclure la surcharge liée à la taille du fichier qui dudoit être exécutée et qui n'est jamais utilisée):

 find ./ -mindepth 2 -type f | cut -d/ -f2 | sort | uniq -c | sort -nr

-mindepth 2Le paramètre est utilisé pour exclure les fichiers du répertoire courant. Si vous le supprimez, vous verrez un tas de lignes comme celle-ci:

  234 dir1
  123 dir2
    1 file1
    1 file2
    1 file3
      ...
    1 fileN

(un peu comme le dufait la variante basée sur la base)

Si vous devez également compter les fichiers dans le répertoire actuel, utilisez cette version améliorée:

{ find ./ -mindepth 2 -type f | cut -d/ -f2 | sort && find ./ -maxdepth 1 -type f | cut -d/ -f1; } | uniq -c | sort -nr

La sortie sera comme suit:

  234 dir1
  123 dir2
   42 .

5

Cela peut également être fait avec une boucle sur ls au lieu de find

for f in */; do echo "$f -> $(ls $f | wc -l)"; done

Explication:

for f in */; - boucle sur tous les répertoires

do echo "$f -> - imprimer chaque nom de répertoire

$(ls $f | wc -l) - appeler ls pour ce répertoire et compter les lignes


1
Cela ne fonctionne pas correctement si les noms de répertoire contiennent des espaces.
Xylol

Essayezfor f ./* ; do echo $f $(ls "$f" | wc -l); done
4ndt3s le

3

Cela devrait renvoyer le nom du répertoire suivi du nombre de fichiers dans le répertoire.

findfiles() {
    echo "$1" $(find "$1" -maxdepth 1 -type f | wc -l)
}

export -f findfiles

find ./ -type d -exec bash -c 'findfiles "$0"' {} \;

Exemple de sortie:

./ 6
./foo 1
./foo/bar 2
./foo/bar/bazzz 0
./foo/bar/baz 4
./src 4

Le export -fest obligatoire car l' -execargument de findne permet pas d'exécuter une fonction bash sauf si vous appelez bash explicitement, et vous devez exporter explicitement la fonction définie dans la portée actuelle vers le nouveau shell.


Cela semble trop compliqué. Il me semble également que cela donne des décomptes cumulatifs pour une hiérarchie de répertoires telle que ./dir1/dir2/dir3(compter les fichiers dans dir1et ses sous-répertoires tous ensemble, plutôt que de compter les fichiers dir1/dir2/dir3séparément de ceux dans dir1/dir2et les deux séparément de ceux dans /dir1).
Jonathan Leffler

J'ai compris que c'était ce que voulait l'auteur. Si tel n'est pas le cas, je conviens que la réponse n'est pas pertinente à la question.
Tuxdude

1
@JonathanLeffler - D'accord, en lisant à nouveau la question, j'ai réalisé que vous avez raison - j'ai modifié la réponse en conséquence.
Tuxdude

2

J'ai combiné la réponse de @glenn jackman et la réponse de @ pcarvalho (dans la liste des commentaires, il y a quelque chose qui ne va pas avec la réponse de pcarvalho parce que la fonction de contrôle de style supplémentaire du caractère `` '(backtick)).

Mon script peut accepter le chemin comme un augument et trier la liste des répertoires comme ls -l, il peut également gérer le problème de "l'espace dans le nom du fichier" .

#!/bin/bash
OLD_IFS="$IFS"
IFS=$'\n'
for dir in $(find $1 -maxdepth 1 -type d | sort); 
do
    files=("$dir"/*)
    printf "%5d,%s\n" "${#files[@]}" "$dir"
done
FS="$OLD_IFS"

Ma première réponse dans stackoverflow, et j'espère que cela pourra aider quelqu'un ^ _ ^


1

trouver . -type f -printf '% h \ n' | trier | uniq -c

donne par exemple:

  5 .
  4 ./aln
  5 ./aln/iq
  4 ./bs
  4 ./ft
  6 ./hot

0

J'ai essayé avec certains des autres ici mais je me suis retrouvé avec des sous-dossiers inclus dans le nombre de fichiers alors que je ne voulais que les fichiers. Ceci s'imprime ./folder/path<tab>nnnavec le nombre de fichiers, sans compter les sous-dossiers, pour chaque sous-dossier du dossier actuel.

for d in `find . -type d -print` 
do 
  echo -e "$d\t$(find $d -maxdepth 1 -type f -print | wc -l)"
done

0

Un moyen facile de rechercher de manière récursive des fichiers d'un type donné. Dans ce cas, fichiers .jpg pour tous les dossiers du répertoire courant:

find . -name *.jpg -print | wc -l


0

Une commande miracle ultra rapide, qui parcourt les fichiers de manière récursive pour compter le nombre d'images dans un répertoire et organiser la sortie par extension d'image:

find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'

Crédits: https://unix.stackexchange.com/a/386135/354980


0

Cela pourrait être une autre façon de parcourir les structures de répertoires et de fournir des résultats détaillés.

find . -type d  | awk '{print "echo -n \""$0"  \";ls -l "$0" | grep -v total | wc -l" }' | sh 

0

J'ai édité le script afin d'exclure tous les node_modulesrépertoires à l'intérieur de celui analysé.

Cela peut être utilisé pour vérifier si le nombre de fichiers du projet dépasse le nombre maximal que l'observateur de fichiers peut gérer.

find . -type d ! -path "*node_modules*" -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

Pour vérifier le nombre maximum de fichiers que votre système peut regarder:

cat /proc/sys/fs/inotify/max_user_watches

node_modules Le dossier devrait être ajouté aux chemins exclus de votre IDE / éditeur dans les systèmes lents, et le nombre d'autres fichiers ne devrait idéalement pas dépasser le maximum (qui peut être changé cependant).


-1

Cela donnera le décompte global.

for file in */; do echo "$file -> $(ls $file | wc -l)"; done | cut -d ' ' -f 3| py --ji -l 'numpy.sum(l)'

Non, ce ne sera pas le cas. Il ne considérera qu'un seul niveau de sous-répertoires.
Kusalananda
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.