Trouver la taille totale de certains fichiers dans une branche de répertoire

140

Supposons qu’il existe un répertoire de stockage d’images, disons, ./photos/john_doedans lequel se trouvent plusieurs sous-répertoires, contenant de nombreux fichiers (par exemple, *.jpg). Comment puis-je calculer une taille de résumé de ces fichiers sous la john_doebranche?

J'ai essayé du -hs ./photos/john_doe/*/*.jpg, mais cela ne montre que les fichiers individuels. En outre, cela ne suit que le premier niveau d'imbrication du john_doerépertoire, comme john_doe/june/, mais ignore john_doe/june/outrageous/.

Alors, comment pourrais-je traverser toute la branche, en résumant la taille de certains fichiers?

mbaitoff
la source

Réponses:

183
find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$

Si plusieurs invocations de dusont requises en raison de la longueur de la liste de fichiers, plusieurs totaux sont consignés et doivent être additionnés.

SHW
la source
7
trouver -iname 'fichier *' -exec du -cb {} + | grep total $ | cut -f1 | coller -sd + - | bc # somme des octets
Michal Čizmazia
3
Si votre système fonctionne sous une autre langue, vous devez remplacer total $ par un autre mot comme razem $ en polonais.
Zbyszek
1
Vous pouvez ajouter LC_ALL=POSIXcomme préfixe pour toujours grep un total comme celui-ci:LC_ALL=POSIX find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Sven
2
Si vous n'utilisez pas -name, remplacez grep par grep -P "\ttotal$"sinon, tous les fichiers se terminant par "total" seront également capturés.
Thdoan
3
@ MichalČizmazia certains obus (par exemple, Git Bash pour Windows) ne sont pas fournis bc, voici donc une solution plus portable:find -name '*.jpg' -type f -exec du -bc {} + | grep total$ | cut -f1 | awk '{ total += $1 }; END { print total }'
thdoan
50
du -ch public_html/images/*.jpg | grep total
20M total

me donne l'utilisation totale de mes .jpgfichiers dans ce répertoire.

Pour gérer plusieurs annuaires, vous devrez probablement combiner cela avec find.

Vous pourriez trouver des exemples de commande utiles (cela inclut aussi find)

Levon
la source
2
Cela ne traverse pas les répertoires sous-jacents?
mbaitoff
C’est plus facile à taper que la solution acceptée, mais ce n’est que demi-exact, cela n’inclut pas les images dans les sous-répertoires. Bon à savoir si tous les fichiers sont dans un seul répertoire.
gbmhunter
@gbmhunter Je pense que si vous ajoutez le paramètre -R à -ch, vous obtiendrez également les sous-répertoires lorsqu’il traverse l’arborescence de manière récursive. Je ne suis pas actuellement devant un ordinateur pour l'essayer, mais pour le confirmer.
Levon
1
Je ne vois pas d’ -Roption sur man7.org/linux/man-pages/man1/du.1.html . Et je ne pense pas qu'une option récursive serait utile dans ce cas, car le shell effectue l'expansion globale avant de transmettre les arguments à du.
gbmhunter
22

Avant tout, vous avez besoin de deux choses:

du -ch -- **/*.jpg | tail -n 1
Gilles
la source
très bonne réponse. Plus simple que d'utiliser find (tant que * ou ** correspond à la structure du répertoire)
Andre de Miranda
Il peut également gérer de très longues listes de fichiers alors que son utilisation findpeut renvoyer des résultats erronés.
Eric Fournie
L’expansion de bash permet de mesurer plusieurs jeux de caractères génériques. du -ch -- ./{dir1,dir2}/*.jpgoudu -ch -- ./{prefix1*,prefix2*}.jpg
J.Money le
@EricFournie Cependant, j'ai eu une Argument list too longerreur lors du traitement d'environ 300k fichiers texte.
xtluo
Le nombre maximal d'arguments pour une commande (dans ce cas, les noms de fichier renvoyés par l'extension générique) peut être vérifié avec getconf ARG_MAX. Si vous en avez plus, vous devrez traiter les fichiers un par un ou par lots avec une boucle for.
Eric Fournie
17

La réponse ultime est:

{ find <DIR> -type f -name "*.<EXT>" -printf "%s+"; echo 0; } | bc

et même une version plus rapide, non limitée par la RAM, mais qui nécessite GNU AWK avec le support bignum:

find <DIR> -type f -name "*.<EXT>" -printf "%s\n" | gawk -M '{t+=$1}END{print t}'

Cette version présente les fonctionnalités suivantes:

  • toutes les capacités de findspécifier les fichiers que vous recherchez
  • prend en charge des millions de fichiers
    • les autres réponses ici sont limitées par la longueur maximale de la liste d'arguments
  • génère seulement 3 processus simples avec un débit de tube minimal
    • beaucoup de réponses ici engendrent des processus C + N, où C est une constante et N le nombre de fichiers
  • ne se soucie pas de la manipulation des cordes
    • cette version ne fait pas de grepping, ni de regexing
    • Eh bien, findfait une simple correspondance générique des noms de fichiers
  • éventuellement la somme des formats en une forme lisible par l' homme (par exemple. 5.5K, 176.7M, ...)
    • faire cela | numfmt --to=si
Jan Chren - rindeal
la source
J'aime la simplicité de cette réponse, même si cela n'a fonctionné que lorsque j'ai introduit des espaces après l'accolade d'ouverture et avant l'accolade de fermeture. Je me demande si cela supportera vraiment un nombre de fichiers 'infiinte' cependant :)
andyb
1
@andyb merci pour les commentaires, les espaces autour des accolades sont en effet nécessaires dans BASH, j'utilise ZSH, donc je ne l'ai pas remarqué. Et le nombre de fichiers est limité par la quantité de RAM disponible sur votre système, car l'utilisation de la mémoire par bc augmente lentement à mesure que les chiffres affluent.
Jan Chren - rindeal
8

Les réponses données jusqu'à présent ne tiennent pas compte du fait que la liste de fichiers passée de find en du peut être si longue que find divise automatiquement la liste en morceaux, ce qui entraîne plusieurs occurrences de total.

Vous pouvez soit grep total(locale!) Et résumer manuellement, ou utiliser une commande différente. Autant que je sache, il n’ya que deux façons d’obtenir un total général (en kilo-octets) de tous les fichiers trouvés par find:
find . -type f -iname '*.jpg' -print0 | xargs -r0 du -a| awk '{sum+=$1} END {print sum}'

Explanation
find . -type f -iname '*.jpg' -print0: Recherchez tous les fichiers portant l'extension jpg indépendamment de la casse (* .jpg, * .JPG, * .Jpg ...) et exportez-les (à terminaison nulle).
xargs -r0 du -a: -r: Xargs appelle la commande même sans arguments passés, ce qui empêche -r. -0 signifie des chaînes terminées par null (non terminées par une nouvelle ligne).
awk '{sum+=$1} END {print sum}': Résume la taille des fichiers générés par la commande précédente

Et pour référence, l’autre voie serait
find . -type f -iname '*.jpg' -print0 | du -c --files0-from=-

Jan
la source
Conseil supplémentaire: sur mon disque dur avec 23428 fichiers (22323 étant des images), la première méthode est exécutée pendant 1 seconde, tandis que la seconde fonctionne pendant 3,8 secondes.
Jan
Notez que les deux supposent un système GNU. La première suppose que les noms de fichiers ne contiennent pas de caractères de nouvelle ligne.
Stéphane Chazelas
Je parie que cela a du --file0-frompris plus de temps parce que vous l'avez exécuté en premier (effet de mise en cache).
Stéphane Chazelas
Avec xargs, plusieurs du -apeuvent être exécutés, de sorte que vous pouvez avoir des divergences s’il existe des liens solides.
Stéphane Chazelas
3

Si la liste de fichiers est trop longue pour pouvoir être transmise à un seul appel de du -c, sur un système GNU, vous pouvez effectuer les opérations suivantes:

find . -iname '*.jpg' -type f -printf '%b\t%D:%i\n' |
  sort -u | cut -f1 | paste -sd+ - | bc

(taille exprimée en nombre de blocs de 512 octets). Comme dus'il essayait de compter les liens durs seulement une fois. Si vous ne vous souciez pas des liens durs, vous pouvez le simplifier pour:

(printf 0; find . -iname '*.jpg' -type f -printf +%b) | bc

Si vous souhaitez utiliser la taille plutôt que l'utilisation du disque, remplacez %bpar %s. La taille sera alors exprimée en octets.

Stéphane Chazelas
la source
-bash: bc: command not foundCentos - Linux 2.6.32-431.el6.x86_64
oui
@yeya, on dirait que votre déploiement CentOS est en panne. bcest une commande POSIX non facultative.
Stéphane Chazelas
1

Les solutions mentionnées jusqu'à présent sont inefficaces (l'exécutif est coûteux) et nécessitent un travail manuel supplémentaire si la liste de fichiers est longue ou si elles ne fonctionnent pas sous Mac OS X. La solution suivante est très rapide et devrait fonctionner sur n'importe quel système. donne la réponse totale en Go (supprimez a / 1024 si vous voulez voir le total en Mo): find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'

hobbydad
la source
Ni standard, -inameni -lsportable, il ne fonctionnera donc sur aucun système . Cela ne fonctionnera pas non plus correctement s'il existe des noms de fichiers ou des cibles de liens symboliques contenant des caractères de nouvelle ligne.
Stéphane Chazelas
Notez également qu'il donne la somme des tailles de fichier, pas leur utilisation du disque. Pour les liens symboliques, il donne la taille des liens symboliques, pas les fichiers vers lesquels ils pointent.
Stéphane Chazelas
1

Améliorer la réponse géniale de SHW pour le rendre compatible avec n’importe quel lieu, comme Zbyszek l’a déjà souligné dans son commentaire:

LC_ALL=C find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
lbo
la source
1

du traverse naturellement la hiérarchie des répertoires et awk peut effectuer le filtrage, ce qui peut suffire:

du -ak | awk 'BEGIN {sum=0} /\.jpg$/ {sum+=$1} END {print sum}'

Cela fonctionne sans GNU.

GeoffP
la source
1
Ceci est plus coûteux car il implique un statappel pour les fichiers qui ne correspondent pas au modèle recherché.
Law29
Seule cette solution fonctionne sur mon mac.
Matthias M