web-dev-qa-db-fra.com

Trouver le nombre de fichiers dans un répertoire

Existe-t-il une méthode sous Linux permettant de calculer le nombre de fichiers d'un répertoire (c'est-à-dire des enfants immédiats) dans O(1) (indépendamment du nombre de fichiers) sans avoir à répertorier d'abord le répertoire? Sinon O (1), existe-t-il un moyen raisonnablement efficace?

Je cherche une alternative à ls | wc -l.

24
yassin

readdir n'est pas aussi cher que vous ne le pensez. Le talent est d’éviter de statuer sur chaque fichier et (éventuellement) de trier la sortie de ls.

/bin/ls -1U | wc -l

évite les alias dans votre Shell, ne trie pas la sortie et répertorie 1 fichier par ligne (pas strictement nécessaire pour canaliser la sortie dans wc).

La question initiale peut être reformulée comme suit: "la structure de données d'un répertoire stocke-t-elle le nombre d'entrées?", À laquelle la réponse est non. Il n'y a pas de moyen plus efficace de compter les fichiers que readdir (2)/getdents (2).

36
Phil

On peut obtenir le nombre de sous-répertoires d'un répertoire donné sans parcourir toute la liste en statant (stat (1) ou stat (2)) sur le répertoire donné et en observant le nombre de liens vers ce répertoire. Un répertoire donné avec N répertoires enfants aura un nombre de liens de N + 2, un lien pour l'entrée ".." de chaque sous-répertoire, plus deux pour le "." et ".." entrées du répertoire donné.

Cependant, il est impossible d’obtenir le nombre total de fichiers (qu’il s’agisse de fichiers ordinaires ou de sous-répertoires) sans parcourir la liste complète - c’est correct.

La commande "/ bin/ls -1U" n'obtiendra cependant pas toutes les entrées. Il obtiendra seulement les entrées de répertoire qui ne commencent pas par le caractère point (.). Par exemple, il ne compterait pas le fichier ".profile" trouvé dans de nombreux répertoires de connexion $ HOME.

On peut utiliser la commande "/ bin/ls -f" ou la commande "/ bin/ls -Ua" pour éviter le tri et obtenir toutes les entrées.

Malheureusement pour vos besoins, la commande "/ bin/ls -f" ou la commande "/ bin/ls -Ua" comptera également le "." et ".." entrées qui se trouvent dans chaque répertoire. Vous devrez soustraire 2 du compte pour éviter de compter ces deux entrées, comme dans les cas suivants:

expr `/bin/ls -f | wc -l` - 2     # Those are back ticks, not single quotes.

L'option --format = single-column (-1) n'est pas nécessaire dans la commande "/ bin/ls -Ua" lors du traitement de la sortie "ls", comme dans "wc" dans ce cas. La commande "ls" écrira automatiquement sa sortie dans une seule colonne si la sortie n'est pas un terminal.

11
pj_

L'option -U pour ls n'est pas dans POSIX, et dans ls d'OS X, sa signification est différente de GNU ls, ce qui signifie que -t et -l utilisent des temps de création plutôt que des temps de modification. -f est dans POSIX en tant qu’extension XSI. Le manuel de GNU ls décrit -f en tant que do not sort, enable -aU, disable -ls --color et -U en tant que do not sort; list entries in directory order.

POSIX décrit -f comme ceci:

Forcez chaque argument à être interprété comme un répertoire et répertoriez le nom trouvé dans chaque emplacement. Cette option doit désactiver -l, -t, -s et -r et activer -a; L'ordre est l'ordre dans lequel les entrées apparaissent dans l'annuaire.

Des commandes telles que ls|wc -l donnent un résultat incorrect lorsque les noms de fichiers contiennent des nouvelles lignes.

En zsh, vous pouvez faire quelque chose comme ceci:

a=(*(DN));echo ${#a}

D (glob_dots) inclut les fichiers dont le nom commence par un point et N (null_glob) empêche la commande de générer une erreur dans un répertoire vide.

Ou le même en bash:

shopt -s dotglob nullglob;a=(*);echo ${#a[@]}

Si IFS contient ASCII chiffres, ajoutez des guillemets autour de ${#a[@]}. Ajoutez shopt -u failglob pour vous assurer que failglob est non défini.

Une option portable consiste à utiliser find:

find . ! -name . -Prune|grep -c /

grep -c / peut être remplacé par wc -l si les noms de fichiers ne contiennent pas de nouvelles lignes. ! -name . -Prune est une alternative portable à -mindepth 1 -maxdepth 1.

Ou voici une autre alternative qui n'inclut généralement pas les fichiers dont le nom commence par un point:

set -- *;[ -e "$1" ]&&echo "$#"

La commande ci-dessus inclut toutefois les fichiers dont le nom commence par un point au cours duquel une option telle que dotglob dans bash ou glob_dots dans zsh est définie. Lorsque * ne correspond à aucun fichier, la commande génère une erreur dans zsh avec les paramètres par défaut.

3
nisetama

J'ai utilisé cette commande .. fonctionne comme un charme..seulement pour changer le maxdepth..that est sous-répertoires

find * -maxdepth 0 -type d -exec sh -c "echo -n {} ' ' ; ls -lR {} | wc -l" \;
2
user2991011

Je pense que vous pouvez avoir plus de contrôle sur ceci en utilisant find:

find <path> -maxdepth 1 -type f -printf "." | wc -c
  • find -maxdepth 1 n'ira pas plus loin dans la hiérarchie des fichiers. 
  • -type f permet de filtrer uniquement les fichiers. De même, vous pouvez utiliser -type d pour les répertoires.
  • -printf "." imprime un point pour chaque match.
  • wc -c compte les caractères, il compte donc les points créés par la print..., ce qui signifie qu'il faut compter combien de fichiers existent dans le chemin donné.
2
fedorqui

Pour autant que je sache, il n'y a pas de meilleure alternative. Ces informations peuvent être hors sujet avec cette question et vous le savez peut-être déjà. Sous Linux (en général sous Unix), les répertoires ne sont que des fichiers spéciaux contenant la liste des autres fichiers (je comprends que les détails exacts dépendront d'un fichier spécifique.) système mais c’est l’idée générale). Et il n'y a pas d'appel pour trouver le nombre total d'entrées sans parcourir toute la liste. S'il vous plaît faites-moi corriger si je me trompe. 

1
taskinoor

Pour le nombre de tous les fichiers dans un répertoire courant, essayez ceci:

ls -lR * | wc -l
0
chry