Найти количество файлов в каталоге - PullRequest
25 голосов
/ 13 сентября 2010

Существует ли какой-либо метод в Linux для вычисления количества файлов в каталоге (то есть непосредственных дочерних элементов) в O (1) (независимо от количества файлов) без предварительного перечисления каталога?Если не O (1), есть ли достаточно эффективный способ?

Я ищу альтернативу ls | wc -l.

Ответы [ 8 ]

37 голосов
/ 13 сентября 2010

readdir не так дорого, как вы думаете. Хитрость заключается в том, чтобы не указывать статистику для каждого файла и (необязательно) сортировать вывод ls.

/bin/ls -1U | wc -l

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

Исходный вопрос можно перефразировать как «хранит ли структура данных каталога количество записей?», На что ответ «нет». Нет более эффективного способа подсчета файлов, чем readdir (2) / getdents (2).

11 голосов
/ 13 сентября 2010

Можно получить количество подкаталогов данного каталога, не просматривая весь список, выполняя stat (стат (1) или стат (2)) данного каталога и наблюдая за количеством ссылок на этот каталог. У заданного каталога с N дочерними каталогами будет количество ссылок N + 2, одна ссылка для записи «..» каждого подкаталога, плюс две для «.» и ".." записи данного каталога.

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

Команда "/ bin / ls -1U" не получит все записи. Он получит только тех записей каталога, которые не начинаются с символа точки (.). Например, он не будет учитывать файл ".profile", найденный во многих каталогах $ HOME для входа в систему.

Можно использовать команду "/ bin / ls -f" или команду "/ bin / ls -Ua", чтобы избежать сортировки и получить все записи.

Возможно, к сожалению, для ваших целей, команда "/ bin / ls -f" или команда "/ bin / ls -Ua" также будут считать "." и ".." записи, которые есть в каждом каталоге. Вам нужно будет вычесть 2 из подсчета, чтобы избежать подсчета этих двух записей, например:

expr `/bin/ls -f | wc -l` - 2     # Those are back ticks, not single quotes.

Опция --format = single-column (-1) необязательна для команды "/ bin / ls -Ua" при передаче по выводу "ls", как в данном случае для "wc". Команда "ls" автоматически запишет свой вывод в один столбец, если вывод не является терминалом.

3 голосов
/ 12 мая 2016

Опция -U для ls отсутствует в POSIX, а в OS X ls она отличается от GNU ls тем, что -t и -l используют времена созданиявместо времени модификации.-f находится в POSIX как расширение XSI.Руководство GNU ls описывает -f как do not sort, enable -aU, disable -ls --color и -U как do not sort; list entries in directory order.

POSIX описывает -f следующим образом:

Форсировать каждый аргументбыть интерпретированным как каталог и перечислить имя, найденное в каждом слоте.Эта опция должна отключить -l, -t, -s и -r и включить -a;порядок - это порядок, в котором записи появляются в каталоге.

Такие команды, как ls|wc -l, дают неверный результат, когда имена файлов содержат символы новой строки.

В zsh вы можете сделать что-то подобное:

a=(*(DN));echo ${#a}

D (glob_dots) включает в себя файлы, имена которых начинаются с точки, а N (null_glob) приводит к тому, что команда не приводит к ошибке в пустом каталоге.

Или то же самое в bash:

shopt -s dotglob nullglob;a=(*);echo ${#a[@]}

Если IFS содержит цифры ASCII, добавьте двойные кавычки вокруг ${#a[@]}.Добавьте shopt -u failglob, чтобы гарантировать, что failglob не установлено.

Переносная опция - использовать find:

find . ! -name . -prune|grep -c /

grep -c / можно заменить на wc -l, если имена файловне содержат переводы строк! -name . -prune является переносимой альтернативой -mindepth 1 -maxdepth 1.

Или вот еще одна альтернатива, которая обычно не включает файлы, имена которых начинаются с точки:

set -- *;[ -e "$1" ]&&echo "$#"

Однако приведенная выше команда включаетфайлы, имена которых начинаются с периода, когда установлена ​​опция, такая как dotglob в bash или glob_dots в zsh.Когда * не соответствует ни одному файлу, команда приводит к ошибке в zsh с настройками по умолчанию.

2 голосов
/ 02 августа 2016

Я думаю, что вы можете иметь больше контроля над этим, используя find:

find <path> -maxdepth 1 -type f -printf "." | wc -c
  • find -maxdepth 1 не будет углубляться в иерархию файлов.
  • -type f позволяет фильтровать только файлы.Аналогично, вы можете использовать -type d для каталогов.
  • -printf "." печатает точку для каждого совпадения.
  • wc -c подсчитывает символы, поэтому он подсчитывает точки, созданные print ... что означает подсчет количества файлов по заданному пути.
2 голосов
/ 11 декабря 2013

Я использовал эту команду .. работает как шарм .. только для изменения maxdepth .. это подкаталоги

find * -maxdepth 0 -type d -exec sh -c "echo -n {} ' ' ; ls -lR {} | wc -l" \;
1 голос
/ 13 сентября 2010

Насколько я знаю, лучшей альтернативы нет.Эта информация может быть не по теме этого вопроса, и вы, возможно, уже знаете, что под Linux (в целом под Unix) каталоги являются просто специальным файлом, который содержит список других файлов (я понимаю, что точные детали будут зависеть от конкретного файласистема, но это общая идея).И нет никакого вызова, чтобы найти общее количество записей без обхода всего списка.Пожалуйста, исправьте меня, если я ошибаюсь.

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

Для номера всех файлов в текущем каталоге попробуйте это:

ls -lR * | wc -l
0 голосов
/ 14 сентября 2010

используйте ls -1 | wc -l

...