Fast Linux File Count для большого количества файлов - PullRequest
113 голосов
/ 15 сентября 2009

Я пытаюсь найти лучший способ узнать количество файлов в определенном каталоге, когда существует очень большое количество файлов (> 100 000).

Когда файлов так много, выполнение "ls | wc -l" занимает довольно много времени. Я считаю, что это потому, что он возвращает имена всех файлов. Я пытаюсь использовать как можно меньше дискового ввода-вывода.

Я экспериментировал с некоторыми сценариями оболочки и Perl, но безрезультатно. Есть идеи?

Ответы [ 18 ]

2 голосов
/ 24 апреля 2018

Fast Linux File Count

Самое быстрое число файлов linux, которое я знаю, это

locate -c -r '/home'

нет необходимости вызывать grep! Но, как уже упоминалось, у вас должна быть свежая база данных (ежедневно обновляемая с помощью задания cron или руководство sudo updatedb).

С найти человека

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

Дополнительно Вы должны знать, что он также считает каталоги как файлы!


Кстати: Если вам нужен обзор ваших файлов и каталогов в вашей системе, наберите

locate -S

Выводит количество каталогов, файлов и т. Д.

2 голосов
/ 28 октября 2016

Я пришел сюда, когда пытался подсчитать файлы в наборе данных ~ 10K папок с ~ 10K файлами в каждой. Проблема многих подходов заключается в том, что они неявно оценивают файлы размером 100 млн., Что занимает целую вечность.

Я позволил себе расширить подход с помощью christopher-schultz , чтобы он поддерживал передачу каталогов через args (его рекурсивный подход также использует stat).

Поместить в файл 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;
}

После gcc -o dircnt_args dircnt_args.c вы можете вызвать его так:

dircnt_args /your/dirs/*

Для файлов 100M в папках по 10 КБ описанное выше выполняется довольно быстро (~ 5 минут для первого запуска, отслеживание в кэше: ~ 23 с).

Единственный другой подход, который завершился менее чем за час, был ls с кешем около 1 минуты: ls -f /your/dirs/* | wc -l. Счетчик выключен парой новых строк за каталог ...

Кроме ожидаемых, ни одна из моих попыток с find не вернулась в течение часа: - /

1 голос
/ 26 июня 2015

ls тратит больше времени на сортировку имен файлов, использование -f для отключения сортировки сэкономит время:

ls -f | wc -l

или вы можете использовать find:

find . -type f | wc -l
1 голос
/ 16 октября 2017

Самый быстрый способ на Linux (вопрос помечен как Linux), это использовать прямой системный вызов. Вот небольшая программа, которая считает файлы (только без директорий) в каталоге. Вы можете считать миллионы файлов, и это примерно в 2,5 раза быстрее, чем "ls -f", и примерно в 1,3-1,5 раза быстрее, чем ответ Кристофера Шульца.

#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;
}

PS: Это не рекурсивно, но вы можете изменить его для достижения этого.

0 голосов
/ 08 августа 2018

Я предпочитаю следующую команду для отслеживания изменений в количестве файлов в каталоге.

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

Команда будет держать окно открытым, чтобы отслеживать количество файлов в каталоге с частотой обновления 0,1 с.

0 голосов
/ 03 июля 2013

Первые 10 директоров с наибольшим количеством файлов.

dir=/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$(find ${dir}${i} \
    -type f | wc -l) => $i,"; } | sort -nr | head -10
0 голосов
/ 21 марта 2018

Вы должны использовать «getdents» вместо ls / find

Вот одна очень хорошая статья, в которой описан подход getdents.

http://be -n.com / SPW / что Вы можете список на миллион-файлов-в-каталог-но-не-с-ls.html

Вот выдержка:

ls и практически любой другой способ перечисления каталога (включая python os.listdir, find.) Полагаются на libc readdir (). Однако readdir () считывает только 32 КБ записей каталога за раз, что означает, что если у вас много файлов в одном каталоге (то есть, 500 М записей каталога), то для чтения всех записей каталога потребуется безумно много времени. особенно на медленном диске. Для каталогов, содержащих большое количество файлов, вам нужно копать глубже, чем инструменты, основанные на readdir (). Вам нужно будет использовать системный вызов getdents () напрямую, а не вспомогательные методы из libc.

Мы можем найти код C для вывода списка файлов, используя getdents () из здесь :

Вам необходимо выполнить две модификации, чтобы быстро вывести список всех файлов в каталоге.

Сначала увеличьте размер буфера с X до 5 мегабайт.

#define BUF_SIZE 1024if (dp->d_ino != 0) printf(...);
5

Затем измените основной цикл, где он печатает информацию о каждом файле в каталоге, чтобы пропустить записи с inode == 0. Я сделал это, добавив

*1024*

В моем случае я действительно заботился только об именах файлов в каталоге, поэтому я также переписал оператор printf (), чтобы вывести только имя файла.

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

Скомпилируйте его (для этого не нужны никакие внешние библиотеки, так что это очень просто сделать)

gcc listdir.c -o listdir

Теперь просто запустите

./listdir [directory with insane number of files]
0 голосов
/ 12 февраля 2016

Я понял, что не использовать обработку памяти, когда у вас есть огромный объем данных, быстрее, чем «обвязать» команды. Таким образом, я сохранил результат в файл и проанализировал его

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l
...