¿Encuentra los 50 directorios principales que contienen más archivos / directorios en su primer nivel?

21

¿Cómo puedo usar findpara generar una lista de directorios que contienen la mayor cantidad de archivos? Me gustaría que la lista sea de mayor a menor. Solo me gustaría que la lista tenga 1 nivel de profundidad, y normalmente ejecuto este comando desde la parte superior de mi sistema de archivos, es decir /.

slm
fuente
Pregunta diferente (en realidad la misma pero diferente), pero ¿la respuesta no resolvería su pregunta también? unix.stackexchange.com/questions/117093/…
Patrick
También relacionado: stackoverflow.com/questions/15216370/… . Esto es en lo que basé mi respuesta original en la pregunta de inodo, aunque creo que mi enfoque ofrece algunas mejoras sobre las que hay.
Graeme
@Patrick: esta es una Q cargada solo para albergar Graemes A. Es cierto que los bits están enterrados en las otras A de Q, pero esto fue para sacar este bit para que se pueda hacer referencia en el futuro.
slm
@slm Entonces, realmente no entiendo por qué esto no es un duplicado. Su respuesta parece ser solo una elaboración de una respuesta sobre otra pregunta. Entonces ahora tenemos 3 preguntas para la misma cosa. Creo que la respuesta en mi enlace también es más limpia. Lanzar un shell para cada directorio encontrado se siente sucio.
Patrick
1
@Patrick, he modificado la respuesta para que la solución GNU no inicie un nuevo shell para cada directorio. Sin embargo, tenga en cuenta que esta es la solución estándar para manejar cualquier nombre de archivo de forma portátil.
Graeme

Respuestas:

17

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

Esto usa dos findcomandos. El primero busca directorios y los canaliza a un whilebucle ejecuta el siguiente hallazgo para cada directorio. El segundo enumera todos los archivos / directorios secundarios en el primer nivel mientras los grepcuenta. El greppermite -print0ser utilizado con el segundo hallazgo ya wcque no tiene un -zequivalente. Esto evita que los nombres de archivo con una nueva línea se cuenten dos veces (aunque usar wcy no -print0no haría mucha diferencia).

El resultado del segundo findse coloca en el argumento para que echoasí y el nombre del directorio se pueda colocar fácilmente en la misma línea (la $(..)construcción recorta automáticamente la nueva línea al final de grep). Las líneas se ordenan por número y los 50 números más grandes se muestran con head.

Tenga en cuenta que esto también incluirá los directorios de nivel superior de los puntos de montaje. Una manera simple de evitar esto es usar un montaje de enlace y luego usar el directorio del montaje. Para hacer esto:

sudo mount --bind / /mnt

Una solución más portátil utiliza una instancia de shell diferente para cada directorio (también se responde aquí ):

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

Salida de muestra:

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

ACTUALIZACIÓN: hice todo eso a continuación, lo cual es genial, pero se me ocurrió una mejor manera de ordenar los directorios por uso de inodo:

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

Y si quieres permanecer en el mismo sistema de archivos que haces:

du --inodes -xS

Aquí hay un ejemplo de salida:

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

AHORA CON LS:

Varias personas mencionaron que no tienen coreutils actualizados y que la opción --inodes no está disponible para ellos. Entonces, aquí está 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

Esto me proporciona resultados prácticamente idénticos al 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:

Creo que la includecosa solo depende del directorio en el que se ve el programa al principio, porque son los mismos archivos y están vinculados. Un poco como la cosa de arriba. Sin embargo, podría estar equivocado al respecto, y agradezco la corrección ...

El método subyacente a esto es que reemplazo cada uno de lslos nombres de archivo con el nombre del directorio que lo contiene en sed.Siguiente ... Bueno, yo también estoy un poco confuso. Estoy bastante seguro de que está contando con precisión los archivos, como puede ver aquí:

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

Haga un directorio de prueba:

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

Algunos directorios secundarios:

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

Crea algunos archivos:

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

Algunos enlaces duros:

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

Mira los enlaces duros:

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

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

Se cuentan solo, pero sube un directorio ...

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

Luego ejecuté mi script de ejecución desde abajo y:

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

Y de Graeme:

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

Así que creo que esto muestra que la única forma de contar inodos es por inodo. Y debido a que contar archivos significa contar inodos, no puede contar doblemente inodos: para contar archivos con precisión, los inodos no pueden contarse más de una vez.

ANTIGUO:

Encuentro esto más rápido y es 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

No es necesario -execpara cada directorio, solo usa el shproceso uno y el otro find. Tengo que obtener el set -- $globderecho de incluir .hiddenarchivos y todo lo demás, pero está muy cerca y muy rápido. Simplemente entraría cden el directorio raíz que debería ser para la comprobación y listo.

Aquí hay una muestra de mi salida ejecutada desde /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

También lo uso seden la parte inferior para recortarlo a los 50 mejores resultados. headsería más rápido, por supuesto, pero también recorto cada línea si es necesario:

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

Es crudo, lo admito, pero fue un pensamiento. Otro dispositivo burdo que uso es el vertido 2>stderrpara ambos findy cddentro 2>/dev/null. Es más limpio que mirar errores de permisos para directorios que no puedo leer sin acceso a la raíz, tal vez debería especificar eso find. Bueno, es un trabajo en progreso.

Ok, entonces arreglé los globos de concha así:

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

Realmente iba a hacer una pregunta sobre cómo podría hacerse, pero mientras escribía el título de la pregunta, el sitio me señaló una pregunta relacionada sugerida donde, he aquí, Stephane ya había intervenido . Entonces eso fue conveniente. Aparentemente, [^.],aunque está bien soportado, no es portátil y debes usar el !bang.que encontré en el comentario de Stephane allí.

De todos modos, simplemente jalar archivos ocultos no fue suficiente, obviamente. Entonces tengo que hacerlo setdos veces para evitar buscar posicionales para el literal $glob. Aún así, no parece afectar el rendimiento en absoluto, y agrega de manera confiable todos los archivos en el directorio.

revs mikeserv
fuente
@Graeme Sabes, sin embargo, ninguna de nuestras soluciones está manejando inodos. Es probable que muchos de esos archivos que estamos enumerando estén vinculados entre sí. Creo que podría hacer esto con ls -iy ... supongo ... probablemente grep... tal vez ... bueno, estás usando, -xdev,que es un comienzo ... uniqy sort?
mikeserv
¿De qué versión duestás corriendo? Mi duno tiene otra --inodesopción.
Patrick
@Patrick, podría querer actualizar, pero actualicé la publicación.
mikeserv
Esa es una característica innovadora :-) Estoy ejecutando 8.21. Parece que se agregó 2013-07-27: git.savannah.gnu.org/gitweb/…
Patrick
Además, si no le importa, ¿podría publicar eso en esta pregunta ? No creo que lo acepte, ya que no es muy portátil, pero votaré positivamente, y sería bueno tener otra solución sobre la cuestión.
Patrick
1

¿Por qué no usar algo como KDirStat? Aunque originalmente fue escrito para KDE, pero también funciona bien con GNOME. Le brinda la mejor vista del número de archivos / directorios y el uso respectivo en la GUI

friendyogi
fuente
1
Buscando el método de línea de comando.
slm