Trouver les 50 meilleurs répertoires contenant le plus de fichiers / répertoires dans leur premier niveau?

21

Comment puis-je utiliser findpour générer une liste de répertoires contenant le plus grand nombre de fichiers. J'aimerais que la liste soit du plus haut au plus bas. Je voudrais seulement que la liste atteigne 1 niveau de profondeur, et j'exécute généralement cette commande à partir du haut de mon système de fichiers, c'est-à-dire /.

slm
la source
Question différente (en fait la même mais posée différemment), mais la réponse ne résoudrait-elle pas aussi votre question? unix.stackexchange.com/questions/117093/…
Patrick
Également lié - stackoverflow.com/questions/15216370/… . C'est ce que j'ai basé ma réponse originale sur la question inode, bien que je pense que mon approche offre quelques améliorations par rapport à celles là-bas.
Graeme
@Patrick - c'est un Q chargé juste pour abriter Graemes A. Vrai, les bits sont enterrés dans les autres Q's A, mais c'était pour faire ressortir ce bit afin qu'il puisse être référencé à l'avenir.
slm
@slm Alors je ne comprends vraiment pas pourquoi ce n'est pas un doublon. Sa réponse ne semble être qu'une élaboration d'une réponse à une autre question. Alors maintenant, nous avons 3 questions pour la même chose. Je pense que la réponse sur mon lien est aussi plus propre. Le lancement d'un shell pour chaque répertoire trouvé semble juste sale.
Patrick
1
@Patrick, j'ai retravaillé la réponse pour que la solution GNU ne démarre pas un nouveau shell pour chaque répertoire. Notez cependant qu'il s'agit de la solution standard pour gérer n'importe quel nom de fichier de manière portable.
Graeme

Réponses:

17

Utilisation des outils GNU:

find / -xdev -type d -print0 |
  while IFS= read -d '' dir; do
    echo "$(find "$dir" -maxdepth 1 -print0 | grep -zc .) $dir"
  done |
  sort -rn |
  head -50

Cela utilise deux findcommandes. Le premier recherche les répertoires et les redirige vers une whileboucle exécute la recherche suivante pour chaque répertoire. Le second répertorie tous les fichiers / répertoires enfants du premier niveau tout en les grepcomptant. Le greppermet -print0d'être utilisé avec la deuxième recherche car wcn'a pas d' -zéquivalent. Cela empêche les noms de fichiers avec une nouvelle ligne d'être comptés deux fois (bien que l'utilisation wcet non ne -print0ferait pas beaucoup de différence).

Le résultat de la seconde findest placé dans l'argument pour echoainsi et le nom du répertoire peut facilement être placé sur la même ligne (la $(..)construction coupe automatiquement la nouvelle ligne à la fin de grep). Les lignes sont ensuite triées par numéro et les 50 plus grands nombres sont indiqués par head.

Notez que cela inclura également les répertoires de niveau supérieur des points de montage. Un moyen simple de contourner ce problème consiste à utiliser un montage de liaison, puis à utiliser le répertoire du montage. Pour faire ça:

sudo mount --bind / /mnt

Une solution plus portable utilise une instance de shell différente pour chaque répertoire (également répondu ici ):

find / -xdev -type d -exec sh -c '
  echo "$(find "$0" | grep "^$0/[^/]*$" | wc -l) $0"' {} \; |
  sort -rn |
  head -50

Exemple de sortie:

9225 /var/lib/dpkg/info
6322 /usr/share/qt4/doc/html
4927 /usr/share/man/man3
2301 /usr/share/man/man1
2097 /usr/share/doc
2097 /usr/bin
1863 /usr/lib/x86_64-linux-gnu
1679 /var/cache/apt/archives
1628 /usr/share/qt4/doc/src/images
1614 /usr/share/qt4/doc/html/images
1308 /usr/share/scilab/modules/overloading/macros
1083 /usr/src/linux-headers-3.13-1-common/include/linux
1071 /usr/src/linux-headers-3.13-1-amd64/include/config
847 /usr/include/qt4/QtGui
774 /usr/include/qt4/Qt
709 /usr/share/man/man8
616 /usr/lib
611 /usr/share/icons/oxygen/32x32/actions
608 /usr/share/icons/oxygen/22x22/actions
598 /usr/share/icons/oxygen/16x16/actions
579 /usr/share/bash-completion/completions
574 /usr/share/icons/oxygen/48x48/actions
570 /usr/share/vim/vim74/syntax
546 /usr/share/scilab/modules/m2sci/macros/sci_files
531 /usr/lib/i386-linux-gnu/wine/wine
530 /usr/lib/i386-linux-gnu/wine/wine/fakedlls
496 /etc/ssl/certs
457 /usr/share/mime/application
454 /usr/share/man/man2
450 /usr/include/qt4/QtCore
443 /usr/lib/python2.7
419 /usr/src/linux-headers-3.13-1-common/include/uapi/linux
413 /usr/share/fonts/X11/misc
413 /usr/include/linux
375 /usr/share/man/man5
374 /usr/share/lintian/overrides
372 /usr/share/cmake-2.8/Modules
370 /usr/share/fonts/X11/75dpi
370 /usr/share/fonts/X11/100dpi
356 /usr/share/icons/gnome/24x24/actions
356 /usr/share/icons/gnome/22x22/actions
356 /usr/share/icons/gnome/16x16/actions
353 /usr/share/icons/gnome/48x48/actions
353 /usr/share/icons/gnome/32x32/actions
341 /usr/lib/ghc/ghc-7.6.3
326 /usr/sbin
324 /usr/share/scilab/modules/compatibility_functions/macros
324 /usr/share/scilab/modules/cacsd/macros
320 /usr/share/terminfo/a
319 /usr/share/i18n/locales
Graeme
la source
11

MISE À JOUR: J'ai fait tout cela ci-dessous, ce qui est cool, mais j'ai trouvé une meilleure façon de trier les répertoires par utilisation des inodes:

du --inodes -S | sort -rh | sed -n \
        '1,50{/^.\{71\}/s/^\(.\{30\}\).*\(.\{37\}\)$/\1...\2/;p}'

Et si vous souhaitez rester dans le même système de fichiers que vous:

du --inodes -xS

Voici quelques exemples de sortie:

15K     /usr/share/man/man3
4.0K    /usr/lib
3.6K    /usr/bin
2.4K    /usr/share/man/man1
1.9K    /usr/share/fonts/75dpi
...
519     /usr/lib/python2.7/site-packages/bzrlib
516     /usr/include/KDE
498     /usr/include/qt/QtCore
487     /usr/lib/modules/3.13.6-2-MANJARO/build/include/config
484     /usr/src/linux-3.12.14-2-MANJARO/include/config

MAINTENANT AVEC LS:

Plusieurs personnes ont indiqué qu'elles n'avaient pas de coreutils à jour et que l'option --inodes n'était pas disponible pour elles. Alors, voici ls:

sudo ls -AiR1U ./ | 
sed -rn '/^[./]/{h;n;};G;
    s|^ *([0-9][0-9]*)[^0-9][^/]*([~./].*):|\1:\2|p' | 
sort -t : -uk1.1,1n |
cut -d: -f2 | sort -V |
uniq -c |sort -rn | head -n10

Cela me donne des résultats à peu près identiques à la ducommande:

DU:

15K     /usr/share/man/man3
4.0K    /usr/lib
3.6K    /usr/bin
2.4K    /usr/share/man/man1
1.9K    /usr/share/fonts/75dpi
1.9K    /usr/share/fonts/100dpi
1.9K    /usr/share/doc/arch-wiki-markdown
1.6K    /usr/share/fonts/TTF
1.6K    /usr/share/dolphin-emu/sys/GameSettings
1.6K    /usr/share/doc/efl/html

LS:

14686   /usr/share/man/man3:
4322    /usr/lib:
3653    /usr/bin:
2457    /usr/share/man/man1:
1897    /usr/share/fonts/100dpi:
1897    /usr/share/fonts/75dpi:
1890    /usr/share/doc/arch-wiki-markdown:
1613    /usr/include:
1575    /usr/share/doc/efl/html:
1556    /usr/share/dolphin-emu/sys/GameSettings:

Je pense que la includechose dépend simplement du répertoire dans lequel le programme regarde en premier - car ce sont les mêmes fichiers et liés en dur. Un peu comme la chose ci-dessus. Je peux me tromper à ce sujet - et je me réjouis de la correction ...

La méthode sous-jacente à cela est que je remplace chacun des lsnoms de fichiers par son nom de répertoire contenant dans la sed.suite de cela ... Eh bien, je suis un peu flou moi-même. Je suis assez certain qu'il compte précisément les fichiers, comme vous pouvez le voir ici:

% _ls_i ~/test
> 100 /home/mikeserv/test/realdir
>   2 /home/mikeserv/test
>   1 /home/mikeserv/test/linkdir

DU DEMO

% du --version
> du (GNU coreutils) 8.22

Créez un répertoire de test:

% mkdir ~/test ; cd ~/test
% du --inodes -S
> 1       .

Quelques répertoires enfants:

% mkdir ./realdir ./linkdir
% du --inodes -S
> 1       ./realdir
> 1       ./linkdir
> 1       .

Créez des fichiers:

% printf 'touch ./realdir/file%s\n' `seq 1 100` | . /dev/stdin
% du --inodes -S
> 101     ./realdir
> 1       ./linkdir
> 1       .

Quelques liens physiques:

% printf 'n="%s" ; ln ./realdir/file$n ./linkdir/link$n\n' `seq 1 100` | 
    . /dev/stdin
% du --inodes -S
> 101     ./realdir
> 1       ./linkdir
> 1       .

Regardez les liens physiques:

% cd ./linkdir
% du --inodes -S
> 101

% cd ../realdir
% du --inodes -S
> 101

Ils sont comptés seuls, mais remontez d'un répertoire ...

% cd ..
% du --inodes -S
> 101     ./realdir
> 1       ./linkdir
> 1       .

Ensuite, j'ai exécuté mon script d'exécution par le bas et:

> 100     /home/mikeserv/test/realdir
> 100     /home/mikeserv/test/linkdir
> 2       /home/mikeserv/test

Et Graeme:

> 101 ./realdir
> 101 ./linkdir
> 3 ./

Je pense donc que cela montre que la seule façon de compter les inodes est par inode. Et parce que compter des fichiers signifie compter des inodes, vous ne pouvez pas compter doublement les inodes - pour compter des fichiers avec précision, les inodes ne peuvent pas être comptés plus d'une fois.

VIEUX:

Je trouve ça plus rapide, et c'est portable:

sh <<-\CMD
    { echo 'here='"$PWD"
        printf 'cd "${here}/%s" 2>/dev/null && {
                set -- 
                for glob in ".[!.]*" "[!.]*" ; do
                    set -- $glob "$@" && 
                        [ -e "./$1" ] || shift
                done    
                printf "%%s\\t%%s\\n" $# "$PWD"
        }\n' $( find . -depth -type d 2>/dev/null )
    } | . /dev/stdin |
    sort -rn | 
    sed -n \
        '1,50{/^.\{71\}/s/^\(.\{30\}\).*\(.\{37\}\)$/\1...\2/;p}'
CMD

Cela n'est pas obligatoire -execpour tous les répertoires - il utilise uniquement un shprocessus ell et un find. Je dois encore obtenir le set -- $globdroit d'inclure des .hiddenfichiers et tout le reste, mais c'est très proche et très rapide. Vous feriez juste cdquelque chose dans votre répertoire racine pour la vérification et c'est parti.

Voici un échantillon de ma sortie de sortie de /usr:

14684   /usr/share/man/man3
4322    /usr/lib
3650    /usr/bin
2454    /usr/share/man/man1
1897    /usr/share/fonts/75dpi
...
557     /usr/share/gtk-doc/html/gtk3
557     /usr/share/doc/elementary/latex
539     /usr/lib32/wine/fakedlls
534     /usr/lib/python2.7/site-packages/bzrlib
500     /usr/lib/python3.3/test

J'utilise également seden bas là pour le couper aux 50 premiers résultats. headserait plus rapide, bien sûr, mais je coupe aussi chaque ligne si nécessaire:

...   
159     /home/mikeserv/.config/hom...hhkdoolnlbekcfllmednbl/4.30_0/plugins
154     /home/mikeserv/.config/hom...odhpcledpamjachpmelml/1.3.11_0/js/ace
...

C'est grossier, certes, mais c'était une pensée. Un autre appareil brut que j'utilise est le dumping 2>stderrpour findet cddans 2>/dev/null. C'est juste plus propre que de regarder les erreurs d'autorisations pour les répertoires que je ne peux pas lire sans accès root - je devrais peut-être le spécifier find. Eh bien, c'est un travail en cours.

Ok, j'ai donc réparé les globes shell comme ceci:

for glob in ".[!.]*" "[!.]*" ; do
    set -- $glob "$@" && 
        [ -e "./$1" ] || shift
done    

J'allais en fait poser une question sur la façon dont cela pourrait être fait, mais pendant que je tapais le titre de la question, le site m'a indiqué une question connexe suggérée où, voilà, Stéphane avait déjà pesé . C'était donc pratique. Apparemment [^.],bien supporté, il n'est pas portable et vous devez utiliser le!bang. j'ai trouvé dans le commentaire de Stéphane.

Quoi qu'il en soit, il n'était évidemment pas suffisant d'extraire des fichiers cachés. Je dois donc le faire setdeux fois afin d'éviter de rechercher des positions pour le littéral $glob. Pourtant, cela ne semble pas affecter les performances du tout, et il ajoute de manière fiable tous les fichiers du répertoire.

mikeserv
la source
@Graeme Vous savez, cependant, aucune de nos solutions ne gère réellement les inodes. Un grand nombre de ces fichiers que nous répertorions sont probablement liés les uns aux autres. Je pense que je pourrais le faire avec ls -iet ... je suppose ... probablement grep... peut-être - eh bien, vous utilisez -xdev,ce qui est un début ... uniqet sort?
mikeserv
Quelle version duutilisez-vous? Mon dun'a pas d' --inodesoption.
Patrick
@Patrick - voudra peut-être mettre à jour - mais j'ai mis à jour le message.
mikeserv
C'est une fonctionnalité de pointe :-) J'utilise 8.21. On dirait qu'il a été ajouté le 27/07/2013: git.savannah.gnu.org/gitweb/…
Patrick
De plus, si cela ne vous dérange pas, pourriez-vous publier cela sur cette question . Je ne pense pas que je vais l'accepter car ce n'est pas très portable, mais je vais voter, et ce serait bien d'avoir une autre solution sur la question.
Patrick
1

Pourquoi ne pas utiliser quelque chose comme KDirStat Bien qu'il ait été écrit à l'origine pour KDE mais qu'il fonctionne bien avec GNOME également Il vous donne la meilleure vue du nombre de fichiers / dir et de l'utilisation respective dans l'interface graphique

friendyogi
la source
1
Vous recherchez la méthode de ligne de commande.
slm