2010-09-13 6 views
23

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

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

+0

Какая часть насчет 'ls | wc -l' не O (1)? – halfdan

+4

ls | wc -l заставит ls делать opendir(), readdir() и, вероятно, stat() для всех файлов. Обычно это будет как минимум O (n). – MarkR

+1

@halfdan: ls выводит все файлы, так что это O (n) – yassin

ответ

33

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

/bin/ls -1U | wc -l

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

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

+0

Чтобы избежать псевдонимов, вы можете Также скажем '\ ls'. Проверьте [\ curl ... | bash ... для чего?) (http://stackoverflow.com/a/15951871/1983854) – fedorqui

1

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

10

Можно получить количество поддиректорий заданного каталога без прохождения всего списка по 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 (-1) не является необходимым на «/ бен/ls -Ua "при подключении вывода" ls ", как в случае с" wc "в этом случае. Команда «ls» будет автоматически записывать свой вывод в один столбец, если вывод не является терминалом.

+0

Согласен, что 'ls -f' лучше, чем' ls -1U' (я думаю, '-f' предназначен для таких piping), но я хочу, чтобы 'ls' имел возможность прекратить каждое имя файла с символом NUL вместо новой строки. – musiphil

+0

на Linux: '-b, --escape print C-style escapes для неграфических символов'; который будет печатать встроенные новые строки как '\ n'. – blalor

-1

использование ls -1 | wc -l

+0

ls -l даст вам дополнительную строку из общего количества блоков, которая будет дополнительной строкой при подсчете. ls -1 не делает. –

+1

@VenkatarameshKommoju, а) вы не объясняете, почему это должно быть лучше, чем 'ls | wc -l' и b) это не так. –

2

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

find * -maxdepth 0 -type d -exec sh -c "echo -n {} ' ' ; ls -lR {} | wc -l" \; 
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 как это:

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

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

В Zsh вы можете сделать что-то вроде этого:

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

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

Или же в Баш:

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 в Баш или glob_dots в zsh. Когда * не соответствует файлу, команда приводит к ошибке в zsh с настройками по умолчанию.

+0

Из вашего ответа я поставил это вместе для Bash: '(unset IFS; shopt -s dotglob nullglob; shopt -u failglob; a = (*); echo $ {# a [@]})' - Обратите внимание на parens, которые вызывают выполнение в подоболочке, таким образом сохраняя параметры текущей оболочки и значение IFS. – ThomasR

1

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

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