Я пытаюсь найти лучший способ найти количество файлов в определенном каталоге, когда существует очень большое количество файлов (> 100 000).
Когда файлов столько, выполнение ls | wc -l
занимает довольно много времени. Я считаю, что это потому, что он возвращает имена всех файлов. Я пытаюсь занять как можно меньше дискового ввода-вывода.
Я экспериментировал с некоторыми сценариями оболочки и Perl безрезультатно. Любые идеи?
Ответы:
По умолчанию
ls
сортирует имена, что может занять некоторое время, если их много. Также не будет вывода, пока все имена не будут прочитаны и отсортированы. Используйтеls -f
опцию, чтобы отключить сортировку.Обратите внимание , что это также позволит
-a
, так.
,..
и другие файлы , начинающиеся с.
будут учитываться.источник
ls
.stat()
вызовомls
для каждого файла.find
не,stat()
таким образом, это работает быстрее.ls -f
тоже нетstat()
. Но, конечно же, оба вариантаls
иfind
вызываются,stat()
когда используются определенные параметры, такие какls -l
илиfind -mtime
.ls -fR | wc -l
Самый быстрый способ - это специальная программа, например:
Из моего тестирования без учета кеширования я запускал каждый из них примерно по 50 раз в одном и том же каталоге, снова и снова, чтобы избежать перекоса данных на основе кеша, и я получил примерно следующие показатели производительности (в реальном времени):
Последняя
dircnt
, это программа, скомпилированная из вышеуказанного источника.РЕДАКТИРОВАТЬ 2016-09-26
По многочисленным просьбам я переписал эту программу, сделав ее рекурсивной, чтобы она переместилась в подкаталоги и продолжала отдельно подсчитывать файлы и каталоги.
Поскольку ясно, что некоторые люди хотят знать, как все это сделать, у меня есть много комментариев в коде, чтобы попытаться понять, что происходит. Я написал это и протестировал на 64-битном Linux, но он должен работать на любой POSIX-совместимой системе, включая Microsoft Windows. Сообщения об ошибках приветствуются; Я рад обновить это, если вы не можете заставить его работать на вашем AIX или OS / 400 или чем-то еще.
Как видите, это много сложнее, чем оригинал, и обязательно так: должна существовать по крайней мере одна функция для рекурсивного вызова, если вы не хотите, чтобы код стал очень сложным (например, управление стеком подкаталогов и обработка его в одном цикле). Поскольку мы должны проверять типы файлов, в игру вступают различия между различными ОС, стандартными библиотеками и т. Д., Поэтому я написал программу, которая пытается работать в любой системе, где она будет компилироваться.
Проверки ошибок очень мало, и сама
count
функция на самом деле не сообщает об ошибках. Единственные вызовы, которые могут действительно потерпеть неудачу, этоopendir
иstat
(если вам не повезло, и у вас есть система, в которой ужеdirent
есть тип файла). Я не параноик по поводу проверки общей длины имен пути subdir, но теоретически система не должна разрешать любое имя пути длиннее, чемPATH_MAX
. Если есть сомнения, я могу это исправить, но это просто дополнительный код, который нужно объяснить тому, кто учится писать C. Эта программа предназначена для того, чтобы быть примером того, как рекурсивно погружаться в подкаталоги.РЕДАКТИРОВАТЬ 2017-01-17
Я включил два изменения, предложенные @FlyingCodeMonkey:
lstat
вместоstat
. Это изменит поведение программы, если у вас есть каталоги с символическими ссылками в каталоге, который вы сканируете. Предыдущее поведение состояло в том, что (связанный) подкаталог будет иметь свое количество файлов, добавленное к общему количеству; новое поведение заключается в том, что связанный каталог будет учитываться как один файл, а его содержимое не будет учитываться.РЕДАКТИРОВАТЬ 2017-06-29
Если повезет, это будет последняя редакция этого ответа :)
Я скопировал этот код в репозиторий GitHub, чтобы немного упростить получение кода (вместо копирования / вставки вы можете просто загрузить исходный код ), плюс он позволяет любому предложить модификацию, отправив извлечение -запрос от GitHub.
Исходный код доступен по лицензии Apache License 2.0. Патчи * добро пожаловать!
источник
gcc -o dircnt dircnt.c
и использовать его так./dircnt some_dir
Вы пытались найти? Например:
источник
find /usr/share | wc -l
(~ 137 000 файлов) примерно на 25% быстрее, чемls -R /usr/share | wc -l
(~ 160 000 строк, включая имена dir, итоги dir и пустые строки) при первом запуске каждого и как минимум в два раза быстрее при сравнении последующих (кэшированных) запусков.find
быстрее, чемls
из-за того, как вы используетеls
. Если вы прекратите сортировкуls
и будетеfind
иметь аналогичные показатели.find, ls и perl протестированы на 40000 файлов: одинаковая скорость (хотя я не пробовал очищать кеш):
и с perl opendir / readdir одновременно:
примечание: я использовал / bin / ls -f, чтобы обойти опцию псевдонима, которая может немного замедлиться, и -f, чтобы избежать упорядочения файлов. ls без -f в два раза медленнее, чем find / perl, за исключением того, что ls используется с -f, похоже, это то же самое время:
Я также хотел бы иметь некоторый сценарий для запроса файловой системы напрямую без всей ненужной информации.
тесты, основанные на ответе Питера ван дер Хейдена, Гленна Джекмана и mark4o.
Томас
источник
ls -l | wc -l
папку на внешнем 2,5-дюймовом жестком диске с 1M файлами, операция завершается примерно за 3 минуты. Во второй раз это занимает 12 секунд IIRC. Также это может потенциально зависеть и от вашей файловой системы. I использовалBtrfs
.$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
Вы можете изменить вывод в соответствии с вашими требованиями, но вот одна строчка из bash, которую я написал для рекурсивного подсчета и представления количества файлов в серии каталогов с числовыми именами.
Это выглядит рекурсивно для всех файлов (не каталогов) в данном каталоге и возвращает результаты в хэш-подобном формате. Простые настройки команды find могут сделать файлы, которые вы хотите подсчитывать, более конкретными и т. Д.
Результаты примерно такие:
источник
ls -1 ${dir}
не будет работать должным образом без дополнительных мест. Кроме того, нет никакой гарантии, что возвращаемое имяls
может быть переданоfind
, посколькуls
экранирует непечатные символы для потребления человеком. (mkdir $'oddly\nnamed\ndirectory'
если вы хотите особенно интересный контрольный пример). Узнайте, почему вы не должны анализировать вывод команды ls (1)Удивительно для меня, но простая находка очень похожа на ls -f
против
Конечно, значения в третьем десятичном знаке немного сдвигаются при каждом выполнении любого из них, поэтому они в основном идентичны. Обратите внимание, однако, что это
find
возвращает одну дополнительную единицу, потому что она считает сам фактический каталог (и, как упоминалось ранее,ls -f
возвращает две дополнительные единицы, поскольку она также учитывает. И ..).источник
Просто добавляю это для полноты картины. Правильный ответ, конечно, уже был опубликован кем-то другим, но вы также можете получить количество файлов и каталогов с помощью древовидной программы.
Запустите команду,
tree | tail -n 1
чтобы получить последнюю строку, в которой будет что-то вроде «763 каталога, 9290 файлов». При этом файлы и папки подсчитываются рекурсивно, за исключением скрытых файлов, которые можно добавить с помощью флага-a
. Для справки, на моем компьютере понадобилось 4,8 секунды, чтобы дерево посчитало весь мой домашний каталог, который был 24777 каталогов, 238680 файлов.find -type f | wc -l
занял 5,3 секунды, на полсекунды дольше, так что я думаю, что дерево довольно конкурентоспособно по скорости.Если у вас нет вложенных папок, дерево - это быстрый и простой способ подсчета файлов.
Кроме того, и просто для удовольствия, вы можете использовать,
tree | grep '^├'
чтобы показывать только файлы / папки в текущем каталоге - это в основном намного более медленная версияls
.источник
Brew install tail
для OS X.tail
уже должен быть установлен в вашей системе Mac OS X.Fast Linux File Count
Самый быстрый счетчик файлов Linux, который я знаю,
Там нет нет необходимости вызывать Grep! Но, как уже упоминалось, у вас должна быть свежая база данных (ежедневно обновляемая заданием cron или вручную
sudo updatedb
).От человека найти
Кроме того, вы должны знать, что он также считает каталоги файлами!
Кстати: если вам нужен обзор ваших файлов и каталогов в вашей системе, введите
Выводит количество каталогов, файлов и т. Д.
источник
Пишу здесь, поскольку у меня недостаточно репутационных баллов, чтобы комментировать ответ, но мне разрешено оставлять свой собственный ответ, что не имеет смысла. Тем не мение...
Что касается ответа Кристофера Шульца , я предлагаю изменить stat на lstat и, возможно, добавить проверку границ, чтобы избежать переполнения буфера:
Предложение использовать lstat - избегать использования символических ссылок, которые могут привести к циклам, если каталог содержит символическую ссылку на родительский каталог.
источник
lstat
было хорошим предложением, и вы заслужили за это карму. Это предложение было включено в мой код, опубликованный выше, а теперь и на GitHub.Вы могли бы попробовать , если используется
opendir()
иreaddir()
вPerl
быстрее. Пример этой функции можно найти здесьисточник
Этот ответ здесь быстрее, чем почти все остальное на этой странице для очень больших, очень вложенных каталогов:
https://serverfault.com/a/691372/84703
locate -r '.' | grep -c "^$PWD"
источник
locate -c -r '/path'
как в решенииЯ пришел сюда, когда пытался сосчитать файлы в наборе данных ~ 10K папок с ~ 10K файлами в каждой. Проблема многих подходов заключается в том, что они неявно оценивают файлы размером 100 млн., Что занимает много времени.
Я взял на себя смелость расширить подход Кристофера-Шульца, чтобы он поддерживал передачу каталогов через args (его рекурсивный подход также использует stat).
Поместите в файл следующее
dircnt_args.c
:После
gcc -o dircnt_args dircnt_args.c
вы можете вызвать его так:На 100M файлах в 10K папках описанное выше выполняется довольно быстро (~ 5 минут для первого запуска, отслеживание в кэше: ~ 23 с).
Единственный подход , который закончил менее чем через час был Ls с примерно 1 мин на кэш:
ls -f /your/dirs/* | wc -l
. Счетчик сбился на пару символов новой строки в каталоге ...Помимо ожидаемого, ни одна из моих попыток не
find
вернулась в течение часа: - /источник
Самый быстрый способ в linux (вопрос помечен как linux) - использовать прямой системный вызов. Вот небольшая программа, которая считает файлы (только без каталогов) в каталоге. Вы можете считать миллионы файлов, и это примерно в 2,5 раза быстрее, чем "ls -f", и примерно в 1,3-1,5 раза быстрее, чем ответ Кристофера Шульца.
PS: Это не рекурсивно, но вы можете изменить его для этого.
источник
opendir
/readdir
, но подозреваю, что в конечном итоге это сводится почти к одному и тому же коду. Выполнение системных вызовов таким образом также не переносимо, и, поскольку Linux ABI нестабилен, программа, скомпилированная в одной системе, не гарантирует правильную работу в другой (хотя это довольно хороший совет компилировать что-либо из исходного кода в любой системе * NIX IMO ). Если скорость является ключевым фактором, это хорошее решение, если оно действительно увеличивает скорость - я не тестировал программы отдельно.ls
тратит больше времени на сортировку имен файлов, использование-f
для отключения сортировки сэкономит время:или вы можете использовать
find
:источник
Я понял, что не использовать обработку памяти, когда у вас есть огромное количество данных, быстрее, чем "конвейерная обработка" команд. Я сохранил результат в файл и проанализировал его.
источник
Вместо ls / find следует использовать "getdent".
Вот одна очень хорошая статья, в которой описан подход гетедентов.
http://be-n.com/spw/you-can-list-a-million-files-in-a-directory-but-not-with-ls.html
Вот выдержка:
ls и практически любой другой способ перечисления каталога (включая python os.listdir, find.) полагаются на libc readdir (). Однако readdir () считывает только 32 КБ записей каталога за раз, что означает, что если у вас много файлов в одном каталоге (то есть, 500 М записей каталога), это займет безумно много времени, чтобы прочитать все записи каталога. , особенно на медленном диске. Для каталогов, содержащих большое количество файлов, вам нужно копать глубже, чем инструменты, которые полагаются на readdir (). Вам нужно будет использовать системный вызов getdent () напрямую, а не вспомогательные методы из libc.
Мы можем найти код C для вывода списка файлов с помощью getdent () отсюда :
Вам нужно будет сделать две модификации, чтобы быстро вывести список всех файлов в каталоге.
Во-первых, увеличьте размер буфера с X примерно до 5 мегабайт.
Затем измените основной цикл, где он печатает информацию о каждом файле в каталоге, чтобы пропустить записи с inode == 0. Я сделал это, добавив
В моем случае я действительно заботился только об именах файлов в каталоге, поэтому я также переписал оператор printf (), чтобы печатать только имя файла.
Скомпилируйте его (ему не нужны внешние библиотеки, поэтому это очень просто сделать)
Теперь просто беги
источник
readdir()
самом деле он не медленный. Мне нужна твердая цифра, прежде чем я пойму, что ради такого увеличения производительности стоит отказаться от переносимости.Я предпочитаю следующую команду, чтобы отслеживать изменения количества файлов в каталоге.
Команда будет держать окно открытым, чтобы отслеживать количество файлов в каталоге с частотой обновления 0,1 с.
источник
ls | wc -l
закончите для папки с тысячами или миллионами файлов за 0,01 с? даже вашls
очень неэффективен по сравнению с другими решениями. И ОП просто хочет получить счетчик, а не сидеть и смотреть, как меняется выходной сигналwatch
руководство после этого комментария и увидел, что 0,01 с (не 0,1 с) - это нереалистичное число, потому что частота обновления большинства экранов ПК составляет всего 60 Гц, и это никоим образом не отвечает на вопрос. ОП спрашивал о «Fast Linux File Count для большого количества файлов». Вы также не читали ни одного доступного ответа перед публикациейПервые 10 директоров с наибольшим количеством файлов.
источник