Recuento rápido de archivos de Linux para una gran cantidad de archivos

137

Estoy tratando de encontrar la mejor manera de encontrar la cantidad de archivos en un directorio en particular cuando hay una gran cantidad de archivos (> 100,000).

Cuando hay tantos archivos, la ejecución ls | wc -ltarda bastante tiempo en ejecutarse. Creo que esto se debe a que está devolviendo los nombres de todos los archivos. Estoy tratando de ocupar la menor cantidad de IO de disco posible.

He experimentado con algunos scripts de shell y Perl en vano. ¿Algunas ideas?

ks1322
fuente
2
asegúrese de que su "ls" sea / usr / bin / ls y no un alias para algo más elegante.
Glenn Jackman
Pregunta similar con respuestas interesantes aquí: serverfault.com/questions/205071/…
aidan
Vale la pena señalar que la mayoría, si no todas las soluciones presentadas a esta pregunta no son específicas de Linux , pero son bastante generales para todos los sistemas * NIX-like. Quizás eliminar la etiqueta "Linux" sea apropiado.
Christopher Schultz

Respuestas:

189

Por defecto lsordena los nombres, lo que puede llevar un tiempo si hay muchos. Además, no habrá salida hasta que se lean y se ordenen todos los nombres. Use la ls -fopción para desactivar la clasificación.

ls -f | wc -l

Tenga en cuenta que esto también permitirá -a, por lo que ., ..y otros archivos que comienzan con .será contado.

mark4o
fuente
11
+1 Y pensé que sabía todo lo que había que saber ls.
mob
55
ZOMG La clasificación de líneas de 100K no es nada, en comparación con la stat()llamada que se lshace en cada archivo. findno funciona stat()así más rápido.
Dummy00001
12
ls -fNo hace stat()tampoco. Pero, por supuesto, ambos lsy findllamar stat()cuando se utilizan ciertas opciones, como ls -lo find -mtime.
mark4o
77
Para el contexto, esto tomó 1-2 minutos para contar 2.5 millones de jpgs en una caja Slicehost pequeña.
philfreo
66
Si desea agregar subdirectorios al recuento, hágalols -fR | wc -l
Ryan Walls
62

La forma más rápida es un programa especialmente diseñado, como este:

#include <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dir;
    struct dirent *ent;
    long count = 0;

    dir = opendir(argv[1]);

    while((ent = readdir(dir)))
            ++count;

    closedir(dir);

    printf("%s contains %ld files\n", argv[1], count);

    return 0;
}

De mis pruebas sin tener en cuenta el caché, ejecuté cada una de estas aproximadamente 50 veces cada una contra el mismo directorio, una y otra vez, para evitar el sesgo de datos basado en caché, y obtuve aproximadamente los siguientes números de rendimiento (en tiempo de reloj real):

ls -1  | wc - 0:01.67
ls -f1 | wc - 0:00.14
find   | wc - 0:00.22
dircnt | wc - 0:00.04

Ese último dircnt, es el programa compilado de la fuente anterior.

EDITAR 2016-09-26

Debido a la demanda popular, he reescrito este programa para que sea recursivo, por lo que caerá en subdirectorios y continuará contando archivos y directorios por separado.

Como está claro que algunas personas quieren saber cómo hacer todo esto, tengo muchos comentarios en el código para tratar de hacer obvio lo que está sucediendo. Escribí esto y lo probé en Linux de 64 bits, pero debería funcionar en cualquier sistema compatible con POSIX, incluido Microsoft Windows. Los informes de errores son bienvenidos; Me complace actualizar esto si no puede hacerlo funcionar en su AIX o OS / 400 o lo que sea.

Como puede ver, es mucho más complicado que el original y necesariamente así: debe existir al menos una función para que se invoque de forma recursiva a menos que desee que el código se vuelva muy complejo (por ejemplo, administrar una pila de subdirectorios y procesarla en un solo bucle). Como tenemos que verificar los tipos de archivos, entran en juego las diferencias entre diferentes sistemas operativos, bibliotecas estándar, etc., por lo que he escrito un programa que intenta ser utilizable en cualquier sistema donde se compilará.

Hay muy poca comprobación de errores, y la countfunción en sí misma no informa errores. Las únicas llamadas que realmente pueden fallar son opendiry stat(si no tienes suerte y tienes un sistema que ya direntcontiene el tipo de archivo). No estoy paranoico acerca de verificar la longitud total de los nombres de ruta de subdirección, pero en teoría, el sistema no debería permitir ningún nombre de ruta que sea más largo que PATH_MAX. Si hay dudas, puedo solucionarlo, pero es solo más código que debe explicarse a alguien que está aprendiendo a escribir C. Este programa pretende ser un ejemplo de cómo sumergirse en subdirectorios de forma recursiva.

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>

#if defined(WIN32) || defined(_WIN32) 
#define PATH_SEPARATOR '\\' 
#else
#define PATH_SEPARATOR '/' 
#endif

/* A custom structure to hold separate file and directory counts */
struct filecount {
  long dirs;
  long files;
};

/*
 * counts the number of files and directories in the specified directory.
 *
 * path - relative pathname of a directory whose files should be counted
 * counts - pointer to struct containing file/dir counts
 */
void count(char *path, struct filecount *counts) {
    DIR *dir;                /* dir structure we are reading */
    struct dirent *ent;      /* directory entry currently being processed */
    char subpath[PATH_MAX];  /* buffer for building complete subdir and file names */
    /* Some systems don't have dirent.d_type field; we'll have to use stat() instead */
#if !defined ( _DIRENT_HAVE_D_TYPE )
    struct stat statbuf;     /* buffer for stat() info */
#endif

/* fprintf(stderr, "Opening dir %s\n", path); */
    dir = opendir(path);

    /* opendir failed... file likely doesn't exist or isn't a directory */
    if(NULL == dir) {
        perror(path);
        return;
    }

    while((ent = readdir(dir))) {
      if (strlen(path) + 1 + strlen(ent->d_name) > PATH_MAX) {
          fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + 1 + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
          return;
      }

/* Use dirent.d_type if present, otherwise use stat() */
#if defined ( _DIRENT_HAVE_D_TYPE )
/* fprintf(stderr, "Using dirent.d_type\n"); */
      if(DT_DIR == ent->d_type) {
#else
/* fprintf(stderr, "Don't have dirent.d_type, falling back to using stat()\n"); */
      sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
      if(lstat(subpath, &statbuf)) {
          perror(subpath);
          return;
      }

      if(S_ISDIR(statbuf.st_mode)) {
#endif
          /* Skip "." and ".." directory entries... they are not "real" directories */
          if(0 == strcmp("..", ent->d_name) || 0 == strcmp(".", ent->d_name)) {
/*              fprintf(stderr, "This is %s, skipping\n", ent->d_name); */
          } else {
              sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
              counts->dirs++;
              count(subpath, counts);
          }
      } else {
          counts->files++;
      }
    }

/* fprintf(stderr, "Closing dir %s\n", path); */
    closedir(dir);
}

int main(int argc, char *argv[]) {
    struct filecount counts;
    counts.files = 0;
    counts.dirs = 0;
    count(argv[1], &counts);

    /* If we found nothing, this is probably an error which has already been printed */
    if(0 < counts.files || 0 < counts.dirs) {
        printf("%s contains %ld files and %ld directories\n", argv[1], counts.files, counts.dirs);
    }

    return 0;
}

EDITAR 2017-01-17

He incorporado dos cambios sugeridos por @FlyingCodeMonkey:

  1. Usar en lstatlugar de stat. Esto cambiará el comportamiento del programa si tiene directorios enlazados en el directorio que está escaneando. El comportamiento anterior era que el subdirectorio (vinculado) tendría su recuento de archivos agregado al recuento general; El nuevo comportamiento es que el directorio vinculado contará como un solo archivo y no se contará su contenido.
  2. Si la ruta de un archivo es demasiado larga, se emitirá un mensaje de error y el programa se detendrá.

EDITAR 2017-06-29

Con suerte, esta será la última edición de esta respuesta :)

He copiado este código en un repositorio de GitHub para que sea un poco más fácil obtener el código (en lugar de copiar / pegar, solo puede descargar la fuente ), además hace que sea más fácil para cualquier persona sugerir una modificación enviando un pull -solicitud de GitHub.

La fuente está disponible bajo la licencia Apache 2.0. Parches * bienvenido!


  • "parche" es lo que las personas mayores como yo llaman una "solicitud de extracción".
Christopher Schultz
fuente
2
¡Simplemente genial! ¡Gracias! Y para aquellos que desconocen: puede complile el código anterior en el terminal: gcc -o dircnt dircnt.cy el uso es como esto./dircnt some_dir
aesede
¿Hay una manera fácil de hacer esto recursivo?
ck_
@ck_ Claro, esto puede hacerse fácilmente recursivo. ¿Necesitas ayuda con la solución o quieres que escriba todo?
Christopher Schultz
1
@ChristopherSchultz, los puntos de referencia que publicó anteriormente: ¿qué tan grande era el directorio en cuestión?
Dom Vinyard el
1
Realmente quería usar esto en Python, así que lo empaqueté como el paquete ffcount . ¡Gracias por hacer que el código esté disponible @ChristopherSchultz!
GjjvdBurg
35

¿Intentaste encontrar? Por ejemplo:

find . -name "*.ext" | wc -l
igustin
fuente
1
Esto buscará archivos de forma recursiva en el directorio actual.
mark4o
En mi sistema, find /usr/share | wc -l(~ 137,000 archivos) es aproximadamente un 25% más rápido que ls -R /usr/share | wc -l(~ 160,000 líneas incluyendo nombres de directorios, totales de directorios y líneas en blanco) en la primera ejecución de cada uno y al menos dos veces más rápido al comparar ejecuciones posteriores (en caché).
Pausado hasta nuevo aviso.
11
Si solo desea el directorio actual, no todo el árbol recursivamente, puede agregar la opción -maxdepth 1 para buscar.
igustin
3
Parece que la razón findes más rápida que lspor la forma en que la está utilizando ls. Si deja de ordenar lsy findtiene un rendimiento similar.
Christopher Schultz
17

find, ls y perl probados contra 40 000 archivos: misma velocidad (aunque no intenté borrar el caché):

[user@server logs]$ time find . | wc -l
42917

real    0m0.054s
user    0m0.018s
sys     0m0.040s
[user@server logs]$ time /bin/ls -f | wc -l
42918

real    0m0.059s
user    0m0.027s
sys     0m0.037s

y con perl opendir / readdir, al mismo tiempo:

[user@server logs]$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"'
42918

real    0m0.057s
user    0m0.024s
sys     0m0.033s

nota: utilicé / bin / ls -f para asegurarme de omitir la opción de alias que podría ralentizarse un poco y -f para evitar el orden de los archivos. ls sin -f es dos veces más lento que find / perl, excepto si ls se usa con -f, parece ser la misma hora:

[user@server logs]$ time /bin/ls . | wc -l
42916

real    0m0.109s
user    0m0.070s
sys     0m0.044s

También me gustaría tener algún script para preguntar directamente al sistema de archivos sin toda la información innecesaria.

pruebas basadas en la respuesta de Peter van der Heijden, glenn jackman y mark4o.

Thomas

Thomas
fuente
55
Definitivamente debe borrar el caché entre pruebas. La primera vez que ejecuto ls -l | wc -luna carpeta en un HDD externo de 2.5 "con archivos de 1M, la operación tarda unos 3 minutos en finalizar. La segunda vez toma 12 segundos IIRC. También esto podría depender de su sistema de archivos. I estaba usando Btrfs.
Behrang Saeedzadeh
Gracias, el fragmento de Perl es la solución para mí. $ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
Pažout
5

Puede cambiar la salida en función de sus requisitos, pero aquí hay un bash one-liner que escribí para contar recursivamente e informar el número de archivos en una serie de directorios con nombres numéricos.

dir=/tmp/count_these/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$i => $(find ${dir}${i} -type f | wc -l),"; }

Esto busca de forma recursiva todos los archivos (no directorios) en el directorio dado y devuelve los resultados en un formato similar al hash. Los ajustes simples al comando de búsqueda podrían hacer que el tipo de archivos que busca contar sea más específico, etc.

Resultados en algo como esto:

1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,
poderosos
fuente
1
El ejemplo me pareció un poco confuso. Me preguntaba por qué había números a la izquierda, en lugar de nombres de directorio. Sin embargo, gracias por esto, terminé usándolo con algunos pequeños ajustes. (contando directorios y soltando el nombre de la carpeta base. para i en $ (ls -1. | sort -n); {echo "$ i => $ (find $ {i} | wc -l)";}
TheJacobTaylor
Los números a la izquierda son los nombres de mi directorio de mis datos de ejemplo. Lo siento, eso fue confuso.
mightybs
1
ls -1 ${dir}no funcionará correctamente sin más espacios. Además, no hay garantía de que lsse pueda pasar el nombre devuelto por find, ya que lsescapa a los caracteres no imprimibles para consumo humano. ( mkdir $'oddly\nnamed\ndirectory'si quieres un caso de prueba particularmente interesante). Vea por qué no debe analizar la salida de ls (1)
Charles Duffy
4

Sorprendentemente para mí, un hallazgo básico es muy comparable a ls -f

> time ls -f my_dir | wc -l
17626

real    0m0.015s
user    0m0.011s
sys     0m0.009s

versus

> time find my_dir -maxdepth 1 | wc -l
17625

real    0m0.014s
user    0m0.008s
sys     0m0.010s

Por supuesto, los valores en el tercer lugar decimal cambian un poco cada vez que ejecuta cualquiera de estos, por lo que son básicamente idénticos. Sin embargo find, tenga en cuenta que devuelve una unidad adicional, ya que cuenta el directorio real en sí (y, como se mencionó anteriormente, ls -fdevuelve dos unidades adicionales, ya que también cuenta. Y ...).

Bogdan Stăncescu
fuente
4

Solo agrego esto por completo. Por supuesto, otra persona ya ha publicado la respuesta correcta, pero también puede obtener un recuento de archivos y directorios con el programa de árbol.

Ejecute el comando tree | tail -n 1para obtener la última línea, que dirá algo así como "763 directorios, 9290 archivos". Esto cuenta los archivos y carpetas de forma recursiva, excluyendo los archivos ocultos, que se pueden agregar con la bandera -a. Como referencia, tardé 4,8 segundos en mi computadora, para que el árbol contara todo mi directorio de inicio, que era 24777 directorios, 238680 archivos. find -type f | wc -ltardó 5.3 segundos, medio segundo más, así que creo que el árbol es bastante competitivo en cuanto a velocidad.

Mientras no tenga ninguna subcarpeta, el árbol es una forma rápida y fácil de contar los archivos.

Además, y solo por el gusto de hacerlo, puede usar tree | grep '^├'para mostrar solo los archivos / carpetas en el directorio actual; esta es básicamente una versión mucho más lenta de ls.

Benubird
fuente
Brew install tailpara OS X.
The Unfun Cat
@TheUnfunCat tailya debería estar instalado en su sistema Mac OS X.
Christopher Schultz
4

Recuento rápido de archivos de Linux

El recuento de archivos de Linux más rápido que conozco es

locate -c -r '/home'

¡No hay necesidad de invocar grep! Pero como se mencionó, debe tener una base de datos nueva (actualizada diariamente por un trabajo cron o manual por sudo updatedb).

Desde el hombre localizar

-c, --count
    Instead  of  writing  file  names on standard output, write the number of matching
    entries only.

¡Además , debe saber que también cuenta los directorios como archivos!


Por cierto: si desea una descripción general de sus archivos y directorios en su tipo de sistema

locate -S

Produce el número de directorios, archivos, etc.

abu_bua
fuente
tenga en cuenta que debe asegurarse de que la base de datos esté actualizada
phuclv
1
LOL si ya tiene todos los recuentos en una base de datos, entonces ciertamente puede contar rápidamente. :)
Christopher Schultz
3

Escribir esto aquí ya que no tengo suficientes puntos de reputación para comentar una respuesta, pero se me permite dejar mi propia respuesta, lo que no tiene sentido. De todas formas...

Sobre la respuesta de Christopher Schultz , sugiero cambiar stat a lstat y posiblemente agregar una verificación de límites para evitar el desbordamiento del búfer:

if (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name) > PATH_MAX) {
    fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
    return;
}

La sugerencia de usar lstat es evitar seguir enlaces simbólicos que podrían conducir a ciclos si un directorio contiene un enlace simbólico a un directorio padre.

FlyingCodeMonkey
fuente
2
Modificando porque el uso de lstatfue una buena sugerencia y mereces karma por ello. Esta sugerencia se incorporó a mi código publicado anteriormente y, ahora, en GitHub.
Christopher Schultz
2

Podrías probar si usar opendir()e readdir()in Perles más rápido. Para ver un ejemplo de esas funciones, mira aquí

Peter van der Heijden
fuente
2
uso: perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar (@files) '
glenn jackman
2

Esta respuesta aquí es más rápida que casi todo lo demás en esta página para directorios muy grandes y muy anidados:

https://serverfault.com/a/691372/84703

locate -r '.' | grep -c "^$PWD"

ck_
fuente
1
Agradable. Como ya tiene una base de datos actualizada de todos los archivos, no es necesario que vuelva a hacerlo. Pero desafortunadamente, debe asegurarse de que el comando updatedb ya se haya ejecutado y completado para este método.
Chris Reid
no necesitas grep. Usar locate -c -r '/path'como en la solución de
abu_bua
2

Vine aquí cuando trataba de contar los archivos en un conjunto de datos de ~ 10K carpetas con ~ 10K archivos cada una. El problema con muchos de los enfoques es que implícitamente almacenan archivos de 100M, lo que lleva años.

Me tomé la libertad de extender el enfoque por Christopher-Schultz para que sea compatible con el paso de directorios a través de args (su enfoque recursivo también usa stat).

Ponga lo siguiente en el archivo dircnt_args.c:

#include <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dir;
    struct dirent *ent;
    long count;
    long countsum = 0;
    int i;

    for(i=1; i < argc; i++) {
        dir = opendir(argv[i]);
        count = 0;
        while((ent = readdir(dir)))
            ++count;

        closedir(dir);

        printf("%s contains %ld files\n", argv[i], count);
        countsum += count;
    }
    printf("sum: %ld\n", countsum);

    return 0;
}

Después de un gcc -o dircnt_args dircnt_args.cpuede invocarlo así:

dircnt_args /your/dirs/*

En archivos de 100M en carpetas de 10K, lo anterior se completa bastante rápido (~ 5 min para la primera ejecución, seguimiento en caché: ~ 23 s).

El único otro enfoque que terminó en menos de una hora era ls con alrededor de 1 min en cache: ls -f /your/dirs/* | wc -l. Sin embargo, el recuento está apagado por un par de nuevas líneas por directorio ...

Aparte de lo esperado, ninguno de mis intentos con find regresó en una hora: - /

Jörn Hees
fuente
Para alguien que no es un programador en C, ¿puede explicar por qué esto sería más rápido y cómo puede obtener la misma respuesta sin hacer lo mismo?
mlissner
no necesita ser un programador en C, solo entienda lo que significa crear un archivo y cómo se representan los directorios: los directorios son esencialmente listas de nombres de archivo e inodos. Si registra un archivo, accede al inodo que está en algún lugar de la unidad para obtener, por ejemplo, información como tamaño de archivo, permisos, .... Si solo está interesado en los recuentos por directorio, no necesita acceder a la información del inodo, lo que podría ahorrarle mucho tiempo.
Jörn Hees
Esto ocurre por defecto en Oracle Linux, gcc versión 4.8.5 20150623 (Red Hat 4.8.5-28.0.1) (GCC) ... las rutas relativas y los archivos remotos parecen ser la causa
Rondo
2

La forma más rápida en Linux (la pregunta está etiquetada como Linux), es utilizar la llamada directa al sistema. Aquí hay un pequeño programa que cuenta archivos (solo, sin directorios) en un directorio. Puede contar millones de archivos y es aproximadamente 2.5 veces más rápido que "ls -f" y aproximadamente 1.3-1.5 veces más rápido que la respuesta de Christopher Schultz.

#define _GNU_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/syscall.h>

#define BUF_SIZE 4096

struct linux_dirent {
    long d_ino;
    off_t d_off;
    unsigned short d_reclen;
    char d_name[];
};

int countDir(char *dir) {


    int fd, nread, bpos, numFiles = 0;
    char d_type, buf[BUF_SIZE];
    struct linux_dirent *dirEntry;

    fd = open(dir, O_RDONLY | O_DIRECTORY);
    if (fd == -1) {
        puts("open directory error");
        exit(3);
    }
    while (1) {
        nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
        if (nread == -1) {
            puts("getdents error");
            exit(1);
        }
        if (nread == 0) {
            break;
        }

        for (bpos = 0; bpos < nread;) {
            dirEntry = (struct linux_dirent *) (buf + bpos);
            d_type = *(buf + bpos + dirEntry->d_reclen - 1);
            if (d_type == DT_REG) {
                // Increase counter
                numFiles++;
            }
            bpos += dirEntry->d_reclen;
        }
    }
    close(fd);

    return numFiles;
}

int main(int argc, char **argv) {

    if (argc != 2) {
        puts("Pass directory as parameter");
        return 2;
    }
    printf("Number of files in %s: %d\n", argv[1], countDir(argv[1]));
    return 0;
}

PD: No es recursivo, pero puedes modificarlo para lograrlo.

Nikolay Dimitrov
fuente
1
No estoy seguro de estar de acuerdo en que esto es más rápido. No he rastreado todo lo que hace el compilador con opendir/ readdir, pero sospecho que al final se reduce a casi el mismo código. Hacer llamadas al sistema de esa manera tampoco es portátil y, como Linux ABI no es estable, un programa compilado en un sistema no garantiza que funcione correctamente en otro (aunque es un buen consejo compilar cualquier cosa desde la fuente en cualquier * NIX system IMO ) Si la velocidad es clave, esta es una buena solución si realmente mejora la velocidad: no he comparado los programas por separado.
Christopher Schultz
1

lspasa más tiempo ordenando los nombres de los archivos, usar -fpara deshabilitar la ordenación ahorrará alguna vez:

ls -f | wc -l

o puedes usar find:

find . -type f | wc -l
Mohammad Anini
fuente
0

Me di cuenta de que no usar el procesamiento de memoria cuando tienes una gran cantidad de datos es más rápido que "canalizar" los comandos. Así que guardé el resultado en un archivo y luego lo analicé

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l
Marcelo Luiz Onhate
fuente
Esta no es la solución más rápida porque los discos duros son extremadamente lentos. Hay otras formas más eficientes que se publicaron años antes que usted
phuclv
0

Debe usar "getdents" en lugar de ls / find

Aquí hay un muy buen artículo que describe el enfoque de getdents.

http://be-n.com/spw/you-can-list-a-million-files-in-a-directory-but-not-with-ls.html

Aquí está el extracto:

ls y prácticamente cualquier otro método para enumerar un directorio (incluido python os.listdir, find.) confía en libc readdir (). Sin embargo, readdir () solo lee 32K de entradas de directorio a la vez, lo que significa que si tiene muchos archivos en el mismo directorio (es decir, 500M de entradas de directorio) tomará un tiempo increíblemente largo leer todas las entradas de directorio , especialmente en un disco lento. Para los directorios que contienen una gran cantidad de archivos, deberá profundizar más que las herramientas que dependen de readdir (). Deberá usar la llamada al sistema getdents () directamente, en lugar de los métodos auxiliares de libc.

Podemos encontrar el código C para enumerar los archivos usando getdents () desde aquí :

Debe realizar dos modificaciones para enumerar rápidamente todos los archivos en un directorio.

Primero, aumente el tamaño del búfer de X a algo así como 5 megabytes.

#define BUF_SIZE 1024*1024*5

Luego modifique el bucle principal donde imprime la información sobre cada archivo en el directorio para omitir las entradas con inode == 0. Lo hice agregando

if (dp->d_ino != 0) printf(...);

En mi caso, también solo me preocupaban los nombres de archivo en el directorio, así que también reescribí la declaración printf () para imprimir solo el nombre del archivo.

if(d->d_ino) printf("%sn ", (char *) d->d_name);

Compílelo (no necesita ninguna biblioteca externa, por lo que es muy sencillo de hacer)

gcc listdir.c -o listdir

Ahora solo corre

./listdir [directory with insane number of files]
Dev123
fuente
Tenga en cuenta que Linux realiza una lectura anticipada, por readdir()lo que en realidad no es lento. Necesito una figura sólida antes de creer que vale la pena tirar la portabilidad para este aumento de rendimiento.
fuz
-1

Prefiero el siguiente comando para realizar un seguimiento de los cambios en la cantidad de archivos en un directorio.

watch -d -n 0.01 'ls | wc -l'

El comando mantendrá una ventana abierta para realizar un seguimiento del número de archivos que están en el directorio con una frecuencia de actualización de 0.1 segundos.

Anoop Toffy
fuente
¿está seguro de que ls | wc -lterminará para una carpeta con miles o millones de archivos en 0.01s? incluso su lses enormemente ineficiente en comparación con otras soluciones. Y el OP solo quiere obtener el recuento, no sentado mirando el cambio de salida
phuclv
Bien. Bien. Encontré una solución elegante que funciona para mí. Me gustaría compartir lo mismo, por lo tanto lo hice. No sé El comando 'ls' en Linux es altamente ineficiente. ¿Qué estás usando en lugar de eso? Y 0.01s es la frecuencia de actualización. No es el momento Si no ha usado el reloj, consulte las páginas de manual.
Anoop Toffy
bueno, leí el watch manual después de ese comentario y vi que 0.01s (no 0.1s) es un número poco realista porque la frecuencia de actualización de la mayoría de las pantallas de PC es de solo 60Hz, y esto no responde la pregunta de ninguna manera. El OP preguntó sobre "Recuento rápido de archivos de Linux para una gran cantidad de archivos". Tampoco leyó ninguna respuesta disponible antes de publicar
phuclv
Leí las respuestas. Pero lo que publiqué es una forma de realizar un seguimiento de la cantidad cambiante de archivos en un directorio. por ejemplo: al copiar archivos de una ubicación a otra, el número de archivos mantiene los cambios. con el método que póster se puede hacer un seguimiento de eso. Acepto que la publicación que hice no modifica ni mejora ninguna publicación anterior.
Anoop Toffy
-2

Primeros 10 directores con el mayor número de archivos.

dir=/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$(find ${dir}${i} \
    -type f | wc -l) => $i,"; } | sort -nr | head -10
usuario2546874
fuente
3
Esto ciertamente se ve asombrosamente similar a la respuesta (con los mismos errores) escrita por mightybs . Si va a extender o modificar el código escrito por otra persona, es apropiado acreditarlo. Comprender el código que está utilizando en sus respuestas lo suficiente como para identificar y corregir sus errores es aún más apropiado.
Charles Duffy