Команда 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.