Найти количество файлов в каталоге
Есть ли какой-либо метод в Linux, чтобы вычислить количество файлов в каталоге (то есть, непосредственных дочерних) в O (1) (независимо от количества файлов), без необходимости сначала перечислить каталог? Если не O (1), существует ли разумно эффективный способ?
Я ищу альтернативу ls | wc -l
.
Ответы
Ответ 1
readdir не так дорого, как вы думаете. Умение избегает статирования каждого файла и (необязательно) сортировки вывода ls.
/bin/ls -1U | wc -l
избегает псевдонимов в вашей оболочке, не сортирует выходные данные и не отображает 1 файл за строку (не обязательно, когда вы отправляете вывод в wc).
Оригинальный вопрос может быть перефразирован как "содержит ли структура данных каталога хранилище количество записей?", на который ответ отрицательный. Нет более эффективного способа подсчета файлов, чем readdir (2)/getdents (2).
Ответ 2
Можно получить количество поддиректорий заданного каталога без прохождения всего списка по stat'ing (stat (1) или stat (2)) данной директории и наблюдению за количеством ссылок на этот каталог. В указанном каталоге с N дочерними каталогами будет указано количество ссылок N + 2, одна ссылка для записи ".." для каждого подкаталога плюс две для ".". и ".." записей данного каталога.
Однако невозможно получить количество всех файлов (будь то обычные файлы или подкаталоги) без прохождения всего списка - это правильно.
Команда "/bin/ls -1U" не получит все записи. Он получит только те записи каталога, которые не начинаются с символа точки (.). Например, он не будет считать файл ".profile", найденный во многих каталогах $HOME 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
Параметр -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 с настройками по умолчанию.
Ответ 4
Я использовал эту команду.. работает как шарм... только для изменения maxdepth.. это подкаталоги
find * -maxdepth 0 -type d -exec sh -c "echo -n {} ' ' ; ls -lR {} | wc -l" \;
Ответ 5
Насколько я знаю, лучшей альтернативы нет. Эта информация может быть не по теме на этот вопрос, и вы, возможно, уже знаете это, что под Linux (вообще под Unix) каталоги - это просто специальный файл, который содержит список других файлов (я понимаю, что точные данные будут зависеть от конкретного файла но это общая идея). И нет вызова, чтобы найти общее количество записей без прохождения всего списка. Пожалуйста, сделайте меня правильным, если я ошибаюсь.
Ответ 6
Я думаю, что вы можете иметь больше контроля над этим, используя find
:
find <path> -maxdepth 1 -type f -printf "." | wc -c
-
find -maxdepth 1
не будет углубляться в иерархию файлов.
-
-type f
позволяет фильтровать только файлы. Аналогично, для каталогов можно использовать -type d
.
-
-printf "."
печатает точку для каждого соответствия.
-
wc -c
подсчитывает символы, поэтому он подсчитывает точки, созданные print
..., что означает подсчет количества файлов в указанном пути.
Ответ 7
использовать ls -1 | wc -l