Encontre os 50 principais diretórios que contêm mais arquivos / diretórios em seu primeiro nível?

21

Como posso usar findpara gerar uma lista de diretórios que contêm o maior número de arquivos. Eu gostaria que a lista fosse do maior para o menor. Eu gostaria apenas que a listagem chegasse a 1 nível de profundidade e, normalmente, executava esse comando a partir do topo do meu sistema de arquivos, ou seja /.

slm
fonte
Pergunta diferente (na verdade a mesma, mas feita de forma diferente), mas a resposta também não resolveria sua pergunta? unix.stackexchange.com/questions/117093/…
Patrick
Também relacionado - stackoverflow.com/questions/15216370/… . É disso que eu baseei minha resposta original na pergunta inode, embora eu ache que minha abordagem ofereça algumas melhorias em relação às que estão lá.
Graeme #
@Patrick - este é um Q carregado apenas para abrigar Graemes A. É verdade que os bits estão enterrados nos outros Q's A, mas isso trouxe esse bit para que pudesse ser referenciado daqui para frente.
Slm
@ SLI Então eu realmente não entendo por que isso não é uma duplicata. Sua resposta parece ser apenas uma elaboração de uma resposta para outra pergunta. Então agora temos três perguntas para a mesma coisa. Eu acho que a resposta no meu link também é mais limpa. Iniciar um shell para todos os diretórios encontrados parece sujo.
Patrick
1
@ Patrick, eu refiz a resposta para que a solução GNU não inicie um novo shell para cada diretório. Embora observe que esta é a solução padrão para lidar com qualquer nome de arquivo de forma portável.
Graeme

Respostas:

17

Usando ferramentas 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

Isso usa dois findcomandos. O primeiro localiza diretórios e os canaliza para um whileloop executa a próxima localização para cada diretório. O segundo lista todos os arquivos / diretórios filhos no primeiro nível enquanto os grepconta. O greppermite -print0ser usado com a segunda localização, pois wcnão possui um -zequivalente. Isso impede que nomes de arquivos com uma nova linha sejam contados duas vezes (embora usar wce não -print0não faça muita diferença).

O resultado do segundo findé colocado no argumento para echoque ele e o nome do diretório possam ser facilmente colocados na mesma linha (a $(..)construção apara automaticamente a nova linha no final de grep). As linhas são classificadas por número e os 50 maiores números mostrados com head.

Observe que isso também incluirá os diretórios de nível superior dos pontos de montagem. Uma maneira simples de contornar isso é usar uma montagem de ligação e, em seguida, usar o diretório da montagem. Para fazer isso:

sudo mount --bind / /mnt

Uma solução mais portátil usa uma instância de shell diferente para cada diretório (também respondida aqui ):

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

Saída de amostra:

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
fonte
11

UPDATE: Fiz tudo isso abaixo, o que é legal, mas criei uma maneira melhor de classificar diretórios pelo uso de inode:

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

E se você quiser permanecer no mesmo sistema de arquivos, faça:

du --inodes -xS

Aqui está um exemplo de saída:

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

AGORA COM LS:

Várias pessoas mencionaram que não possuem coreutils atualizados e a opção --inodes não está disponível para eles. Então, aqui está o sl:

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

Isso está me fornecendo resultados praticamente idênticos ao ducomando:

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:

Eu acho que a includecoisa depende apenas de qual diretório o programa parece primeiro - porque eles são os mesmos arquivos e têm links físicos. Tipo como a coisa acima. Eu posso estar errado sobre isso - e congratulo-me com a correção ...

O método subjacente a isso é que eu substituo cada um dos lsnomes de arquivos pelo nome de diretório que contém em sed.Seguindo a partir disso ... Bem, eu também sou um pouco confusa. Tenho certeza de que está contando com precisão os arquivos, como você pode ver aqui:

% _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

Faça um diretório de teste:

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

Alguns diretórios filhos:

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

Faça alguns arquivos:

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

Alguns hardlinks:

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

Veja os hardlinks:

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

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

Eles são contados sozinhos, mas sobe um diretório ...

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

Em seguida, executei meu script de baixo e:

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

E Graeme's:

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

Então, acho que isso mostra que a única maneira de contar inodes é por inode. E como contar arquivos significa contar inodes, você não pode contar inodes duplamente - contar arquivos com inodes com precisão não pode ser contado mais de uma vez.

VELHO:

Acho isso mais rápido e é portátil:

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

Não é necessário -execpara todos os diretórios - ele usa apenas o shprocesso one ell e one find. Ainda tenho o set -- $globdireito de incluir .hiddenarquivos e tudo mais, mas é muito próximo e muito rápido. Você entraria cdem qualquer diretório raiz para a verificação e pronto.

Aqui está uma amostra da minha saída executada em /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

Também uso sedna parte inferior para apará-lo para os 50 melhores resultados. headseria mais rápido, é claro, mas também aparo cada linha, se necessário:

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

É rude, é verdade, mas foi um pensamento. Outro dispositivo bruto que eu uso é despejar 2>stderrpara ambos finde cdpara dentro 2>/dev/null. É mais limpo do que procurar erros de permissão em diretórios que não consigo ler sem acesso root - talvez eu deva especificar isso find. Bem, é um trabalho em andamento.

Ok, então eu corrigi os globs do shell assim:

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

Na verdade, eu ia fazer uma pergunta sobre como isso poderia ser feito, mas, enquanto digitava o título da pergunta, o site me indicou uma pergunta relacionada sugerida , onde eis que Stephane já pesava . Então isso foi conveniente. Aparentemente, [^.],embora seja bem suportado, não é portátil e você deve usar o !bang.que achei no comentário de Stephane lá.

Enfim, apenas puxar arquivos ocultos não era suficiente, obviamente. Então eu tenho que fazer o setdobro para evitar procurar posicionais pelo literal $glob. Ainda assim, ele não parece afetar o desempenho e adiciona todos os arquivos no diretório de maneira confiável.

mikeserv
fonte
@ Graeme Você sabe, no entanto, nenhuma das nossas soluções está lidando com inodes. Muitos desses arquivos que listamos provavelmente estão vinculados entre si. Eu acho que eu poderia fazer isso com ls -ie ... eu acho ... provavelmente grep... talvez - bem, você está usando o -xdev,que é um começo ... uniqe sort?
mikeserv
Qual versão duvocê está executando? Meu dunão tem --inodesopção.
Patrick Patrick
@ Patrick - pode querer atualizar - mas eu atualizei o post.
precisa saber é o seguinte
Esse é um recurso de ponta :-) Estou executando o 8.21. Parece que foi adicionado 27/07/2013
Patrick
Além disso, se você não se importa, poderia postar isso nesta pergunta . Acho que não vou aceitá-lo, pois não é muito portátil, mas vou votar de novo, e seria bom ter outra solução sobre a questão.
Patrick
1

Por que não usar algo como KDirStat Embora tenha sido originalmente escrito para o KDE, mas também funciona bem com o GNOME Ele oferece uma melhor visualização do número de arquivos / diretórios e do uso respectivo na GUI

friendyogi
fonte
1
Procurando pelo método de linha de comando.
Slm