Команда ls не работает для каталога с огромным количеством файлов


70

У меня был каталог, в котором было около 5 миллионов файлов. Когда я попытался запустить lsкоманду из этого каталога, моя система заняла огромное количество памяти и через некоторое время зависла. Есть ли эффективный способ перечислить файлы, кроме использования lsкоманды?


11
Убедитесь, что у вас нет псевдонима для lsэтого использования --colorили -Fкак это будет означать выполнение lstat(2)для каждого файла.
— Стефан Шазелас

4
Кстати, хранить миллионы файлов в одном каталоге - довольно плохая идея. Если вы контролируете макет каталога, возможно, разделить его по некоторым критериям?
— d33tah

Это был чистый lsзвонок или вы использовали опции?
— Hauke ​​Laging

1
@ d33tah Да, 5 миллионов это много! Моя корневая файловая система имеет ограничение в 7 миллионов inode.
— Микель

7
5 миллионов элементов для вывода - как вы смотрите на это - простого списка слишком много, чтобы увидеть - так для чего вы хотите этот список?
— user151019

Ответы:


66

Избегайте сортировки с помощью:

ls --sort=none # "do not sort; list entries in directory order"

Или, что эквивалентно:

ls -U

10
Интересно, сколько накладных расходов добавляет макет колонки? Добавление -1флага может помочь.
— Микель

Вероятно, не так много, но каждый немного помогает, верно? :)
— Микель

1
@Mikel Это только предположение, или ты это измерил? Мне кажется, это -1занимает еще больше времени.
— Хауке Лагинг

10
«-1» помогает совсем немного. «ls -f -1» позволит избежать любых вызовов статистики и сразу же распечатать все. Вывод столбца (который используется по умолчанию при отправке на терминал) сначала делает его буферизированным. В моей системе, используя btrfs в каталоге с 8 миллионами файлов (как создано "seq 1 8000000 | xargs touch"), "time ls -f -1 | wc -l" занимает менее 5 секунд, а "time ls -f" -C | wc -l "занимает более 30 секунд.
— Скотт Лэмб

1
@ToolmakerSteve Поведение по умолчанию ( -Cкогда stdout является терминалом, -1когда это канал), сбивает с толку. Когда вы экспериментируете и измеряете, вы переключаетесь между просмотром вывода (чтобы убедиться, что команда делает то, что вы ожидаете) и его подавлением (чтобы избежать мешающего фактора пропускной способности терминального приложения). Лучше использовать команды , которые ведут себя таким же образом в обоих режимах, так явно определить формат вывода через -1, -C, -lи т.д.
— Скотт Lamb

47

lsна самом деле сортирует файлы и пытается перечислить их, что приводит к огромным накладным расходам, если мы пытаемся перечислить более миллиона файлов в каталоге. Как упомянуто в этой ссылке, мы можем использовать straceили, findчтобы перечислить файлы. Однако эти варианты также казались неосуществимыми для моей проблемы, поскольку у меня было 5 миллионов файлов. После некоторого поиска в Google, я обнаружил, что если мы перечислим использование каталогов getdents(), это должно быть быстрее, потому что ls, findи Pythonбиблиотеки используют readdir()медленнее, но используют getdents()снизу.

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

/*
 * List directories using getdents() because ls, find and Python libraries
 * use readdir() which is slower (but uses getdents() underneath.
 *
 * Compile with 
 * ]$ gcc  getdents.c -o getdents
 */
#define _GNU_SOURCE
#include <dirent.h>     /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

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

#define BUF_SIZE 1024*1024*5

int
main(int argc, char *argv[])
{
   int fd, nread;
   char buf[BUF_SIZE];
   struct linux_dirent *d;
   int bpos;
   char d_type;

   fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
   if (fd == -1)
       handle_error("open");

   for ( ; ; ) {
       nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
       if (nread == -1)
           handle_error("getdents");

       if (nread == 0)
           break;

       for (bpos = 0; bpos < nread;) {
           d = (struct linux_dirent *) (buf + bpos);
           d_type = *(buf + bpos + d->d_reclen - 1);
           if( d->d_ino != 0 && d_type == DT_REG ) {
              printf("%s\n", (char *)d->d_name );
           }
           bpos += d->d_reclen;
       }
   }

   exit(EXIT_SUCCESS);
}

Скопируйте вышеуказанную C-программу в каталог, в котором должны быть перечислены файлы. Затем выполните следующие команды.

gcc  getdents.c -o getdents
./getdents

Пример синхронизации : getdentsможет быть намного быстрее, чем ls -f, в зависимости от конфигурации системы. Вот некоторые моменты, демонстрирующие увеличение скорости в 40 раз для вывода каталога, содержащего около 500 тыс. Файлов, по монтированию NFS в вычислительном кластере. Сначала каждая команда запускалась 10 раз подряд getdents, потом ls -f. Первый запуск выполняется значительно медленнее, чем все остальные, возможно, из-за ошибок страницы кэширования NFS. (Кроме того, над этим монтированием d_typeполе ненадежно в том смысле, что многие файлы отображаются как «неизвестный» тип.)

command: getdents $bigdir
usr:0.08 sys:0.96  wall:280.79 CPU:0%
usr:0.06 sys:0.18  wall:0.25   CPU:97%
usr:0.05 sys:0.16  wall:0.21   CPU:99%
usr:0.04 sys:0.18  wall:0.23   CPU:98%
usr:0.05 sys:0.20  wall:0.26   CPU:99%
usr:0.04 sys:0.18  wall:0.22   CPU:99%
usr:0.04 sys:0.17  wall:0.22   CPU:99%
usr:0.04 sys:0.20  wall:0.25   CPU:99%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
command: /bin/ls -f $bigdir
usr:0.53 sys:8.39  wall:8.97   CPU:99%
usr:0.53 sys:7.65  wall:8.20   CPU:99%
usr:0.44 sys:7.91  wall:8.36   CPU:99%
usr:0.50 sys:8.00  wall:8.51   CPU:100%
usr:0.41 sys:7.73  wall:8.15   CPU:99%
usr:0.47 sys:8.84  wall:9.32   CPU:99%
usr:0.57 sys:9.78  wall:10.36  CPU:99%
usr:0.53 sys:10.75 wall:11.29  CPU:99%
usr:0.46 sys:8.76  wall:9.25   CPU:99%
usr:0.50 sys:8.58  wall:9.13   CPU:99%

14
Не могли бы вы добавить небольшой эталон времени, для которого ваш случай отображается ls?
— Бернхард

1
Сладкий. И вы можете добавить опцию, чтобы просто считать записи (файлы), а не перечислять их имена (сохраняя миллионы вызовов для printf, для этого списка).
— ChuckCottrill

29
Вы знаете, что ваш каталог слишком велик, когда вам нужно написать собственный код для перечисления его содержимого ...
— casey

1
@casey За исключением того, что вы не должны. Все эти разговоры о getdentsпротив readdirупущены.
— Микель

9
Давай! Там уже есть 5 миллионов файлов. Поместите вашу собственную программу "ls" в другой каталог.
— Йохан

12

Наиболее вероятной причиной , почему это медленный является тип файла окраски, вы можете избежать этого с \lsили /bin/lsотключив параметры цвета.

Если у вас действительно так много файлов в директории, использование findвместо этого также является хорошим вариантом.


7
Я не думаю, что это должно было быть понижено. Сортировка является одной из проблем, но даже без сортировки, ls -U --colorэто займет много времени, так как это будет statкаждый файл. Так что оба верны.
— Микель

Отключение раскраски оказывает огромное влияние на производительность lsи по умолчанию имеет псевдонимы .bashrc.
— Виктор Шредер

Да, я сделал /bin/ls -Uи получил результат в
— кратчайшие

-3

Я считаю, что это echo *работает намного быстрее, чем ls. YMMV.


4
Оболочка будет сортировать *. Так что этот путь, вероятно, все еще очень медленный для 5 миллионов файлов.
— Микель

3
@Mikel Более того, я почти уверен, что 5 миллионов файлов - это более чем тот момент, когда глобализация полностью прекратится.
— evilsoup

4
Минимальная длина имени файла (для 5 миллионов файлов) составляет 3 символа (возможно, 4, если вы придерживаетесь более общих символов) плюс разделители = 4 символа на файл, то есть 20 МБ аргументов команды. Это значительно превышает общую длину расширенной командной строки в 2 МБ. Exec (и даже встроенные) будут оплакивать.
— Йохан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.