Как лучше всего подсчитать количество файлов в каталоге?

11

Если синтаксический анализ выходных данных lsопасен, поскольку он может разбиваться на некоторые забавные символы (пробелы \n, ...), каков наилучший способ узнать количество файлов в каталоге?

Я обычно полагаюсь на то, findчтобы избежать этого анализа, но, аналогично, find mydir | wc -lсломается по тем же причинам.

Я сейчас работаю над Solaris, но я ищу ответ, который был бы более разборчивым для разных юнитов и разных оболочек.

rahmu
источник
3
Я не уверен, что это дубликат, я что-то упустил?
Рахму
1
Это может быть дубликатом, но не указанного вопроса. findполучит рекурсивное количество файлов (используйте, -maxdepth 1если вы этого не хотите. find mydir -maxdepth 1 -type f -printf \\n | wc -lдолжны обрабатывать специальные символы в имени файла, так как они никогда не печатаются в первую очередь.
Anthon

Ответы:

16

Как насчет этого трюка?

find . -maxdepth 1 -exec echo \; | wc -l

Такой же портативный, как findи wc.

rozcietrzewiacz
источник
5
Это не работает ( n+1в моей системе Debian отображаются файлы). Он также не фильтрует обычные файлы.
Крис Даун
4
Я просто дал общий пример. Это работает, но как это работает, зависит от того, как вы адаптируете findкоманду к вашим конкретным потребностям. Да, этот включает в себя все каталоги, в том числе .(возможно, поэтому вы видите результат как n+1).
rozcietrzewiacz
Мне нравится этот трюк, очень умный; но я удивлен, что нет простого и простого способа сделать это!
Рахму
3
@ChrisDown OP не указывает фильтрацию для обычных файлов, запрашивает количество файлов в каталоге. Чтобы избавиться от проблемы n + 1, используйте find . -maxdepth 1 ! -name . -exec echo \; | wc -l; некоторые старые версии findне имеют -not.
Arcege
3
Обратите внимание, что -maxdepthэто не стандарт (расширение GNU теперь также поддерживается несколькими другими реализациями).
Стефан Шазелас
11

С Bash, без внешних утилит, ни петли:

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

В кш заменить shopt -s dotglobна FIGNORE=.?(.). В zsh замени его setopt glob_dotsили убери shoptзвонок и используй files=(*(D)). (Или просто отбросьте строку, если вы не хотите включать точечные файлы.) Если вы не заботитесь о точечных файлах:

set -- *
echo $#

Если вы хотите включить точечные файлы:

set -- *
if [ -e "$1" ]; then c=$#; else c=0; fi
set .[!.]*
if [ -e "$1" ]; then c=$((c+$#)); fi
set ..?*
if [ -e "$1" ]; then c=$((c+$#)); fi
echo $c
enzotib
источник
2
Первый пример печатает 1пустой каталог, когда nullglobон не включен. В Zsh, a=(*(DN));echo ${#a}с N( nullglob) Классификатор не приводит к ошибке для пустого каталога.
нисетама
8
find . ! -name . -prune -print | grep -c /

Должен быть достаточно переносимым для систем после 80-х годов.

Это считает все записи каталога, кроме .и ..в текущем каталоге.

Для подсчета файлов в подкаталогах также:

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

(что нужно переносить даже на Unix V6 (1975), так как это не нужно -prune)

Стефан Шазелас
источник
Один из редких портативных ответов на этой странице, если не единственный.
Ксиэнн
Я вчера проголосовал за этот ответ, так как нашел, что он также хорошо работает для каталогов, отличных от текущего ( find dirname ! -name dirname -prune -print). С тех пор я задавался вопросом, есть ли какая-либо конкретная причина для использования grep -c /вместо wc -l(которая, вероятно, чаще используется для подсчета).
Энтони Геогеган
1
find dirname ! -name dirnameне работает, если есть другие каталоги с именами dirname. Это лучше использовать find dirname/. ! -name .. wc -lподсчитывает количество строк, имена файлов могут состоять из нескольких строк, так как символ новой строки так же действителен, как и любой в имени файла.
Стефан Шазелас
6

Пытаться:

ls -b1A | wc -l

В нем -bбудут непечатаемые символы, -Aбудут показаны все файлы, кроме .и ..одного, и по одному на строку (по умолчанию для канала, но хорошо, если это будет явно).

Пока мы включаем языки сценариев более высокого уровня, в Python есть одна строка:

python -c 'import os; print len(os.listdir(os.sep))'

Или с полным «найти»:

python -c 'import os; print len([j for i in os.walk(os.sep) for j in i[1]+i[2]])'
Arcege
источник
1

Йок может использовать такую ​​конструкцию:

I=0; for i in * ; do ((I++)); done ; echo $I

Но я боюсь, вы можете получить ошибку, как Argument list too long.в случае, если у вас слишком много файлов в каталоге. Однако я проверил его в каталоге с 10 миллиардами файлов, и он работал хорошо.

порыв
источник
3
Это не будет работать и для скрытых файлов, если только оболочка не настроена для их расширения *.
Лекенштейн
gnu find . -maxdepth 1 -type f | wc -l
Нихил Мюлли
4
@Rush: эта команда никогда не должна вызывать «слишком длинный список аргументов». Это происходит только с внешней командой (так никогда с for.
enzotib
1

Рассматривали ли вы Perl, который должен быть относительно портативным?

Что-то типа:

use File::Find;

$counter = 0;

sub wanted { 
  -f && ++$counter
}

find(\&wanted, @directories_to_search);
print "$counter\n";
CJC
источник
0

Попробуйте это => Использование ls с опциями -i (для номера узла) и -F (добавляет имя каталога с помощью '/').

ls -ilF | egrep -v '/' | wc -l
Saumil
источник
0

С perlоднострочником (переформатировано для удобства чтения):

perl -e 'opendir($dh, ".");
         while ( readdir($dh) ) {$count++};
         closedir $dh;
         print "$count\n";'

или

perl -e 'opendir($dh, ".");
         @files = readdir($dh);
         closedir $dh;
         print $#files+1,"\n";'

Вы можете использовать perlфункции, которые изменяют массивы как grepили mapсо второй версией. Смотрите perldoc -f readdirпример использования grep.

саз
источник
0

Самая простая версия, которую я использую все время и с которой никогда не было проблем: ls -b1 | wc -l

Питер
источник
Вы можете столкнуться с проблемами, если имя файла содержит один \nили несколько символов в стиле фанк (да, некоторые юниты допускают это).
Рахму
1
Я попытался это явно, прежде чем опубликовать свой ответ и не было никаких проблем с ним. Я использовал файловый менеджер nautilus, чтобы переименовать файл, содержащий \ n, чтобы попробовать это.
Питер
Вы правы, это не работает так. Я не знаю, что я сделал, когда я впервые проверил это. Попробовал еще раз и обновил мой ответ.
Питер
Нет, команда в порядке, но подобное решение уже существует и скрытые файлы не учитываются.
Ксиэнн
0

В дополнение к findоснованному на Стефане ответу на основе ниже приведен POSIX-совместимый ответ, основанный на ls:

ls -qf | tail -n +3 | wc -l
xhienne
источник