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

106

Я могу перечислить все каталоги по

find ./ -type d

Я попытался перечислить содержимое каждого каталога и подсчитать количество файлов в каждом каталоге, используя следующую команду

find ./ -type d | xargs ls -l | wc -l

Но это суммировало общее количество строк, возвращаемых

find ./ -type d | xargs ls -l

Есть ли способ подсчитать количество файлов в каждом каталоге?

user784637
источник
Вы ищете способ подсчитать количество файлов в каждом из подкаталогов, находящихся непосредственно под ним ./?
Tuxdude 05
5
Как это вопрос не по теме ?? Хотелось бы видеть обоснованные комментарии близких избирателей! Если это не по теме, то к чему это? супер пользователь? Я так не думаю ..
InfantPro'Aravind '
6
shell-скрипт, пакетный скрипт находятся в области программирования!
InfantPro'Aravind '
Я собирался опубликовать решение Pythonic, но заметил, что вопрос закрыт.
анатолий техтоник
проголосовали за его повторное открытие. Могут быть и другие ответы, которые могут быть полезны во многих ситуациях (включая программирование сценариев, по этой причине я задал этот вопрос).
lepe

Ответы:

112

Предполагая, что у вас есть GNU find, позвольте ему найти каталоги, а bash сделает все остальное:

find . -type d -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done
Гленн Джекман
источник
2
Его версия немного отличается от приведенной выше, поэтому: (подсказка: отсортировано по имени и в CSV) для x в find . -maxdepth 1 -type d | sort ; сделать y = find $x | wc -l; эхо $ x, $ y; done
pcarvalho
5
Великий! Поместите его в одну строку (так что это удобно для прямого использования в оболочке):find . -type d -print0 | while read -d '' -r dir; do files=("$dir"/*); printf "%5d files in directory %s\n" "${#files[@]}" "$dir"; done
lucaferrario
14
Мне нужно было получить количество всех файлов (рекурсивно подсчитать) в каждом подкаталоге. Эта модификация дает вам: find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find $dir -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
OmidS
1
@Kory Это сделает следующее:find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done | sort -rn -k1
OmidS
1
@OmidS Отличный одиночный карандаш, но его $dirследует заключать в кавычки в первом комментарии, чтобы правильно обрабатывать имена каталогов с пробелами . :find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
Радек Данилюк
184

Это печатает количество файлов для каждого каталога для текущего уровня каталогов:

du -a | cut -d/ -f2 | sort | uniq -c | sort -nr
Себастьян Пискорски
источник
9
Безусловно, лучшее (и наиболее элегантное) решение, если кто-то хочет рекурсивно перечислить количество файлов в каталогах верхнего уровня.
itoctopus
13
У этого есть две проблемы: он считает на один файл на каталог больше, чем есть на самом деле, и дает бесполезную строку, содержащую размер текущего каталога как «1 размер ». Оба могут быть исправлены с помощью du -a | sed '/.*\.\/.*\/.*/!d' | cut -d/ -f2 | sort | uniq -c. Добавляйте | sort -nrдля сортировки по количеству, а не по имени каталога.
десерт
3
Хочу отметить, что это работает и в OSX. (Просто скопировать совет по Linux в оболочку OSX обычно не работает.)
Пистос,
2
он извлекает ненужный размер с помощью du -a. Лучше использовать команду find. но основная идея точно такая же :)
Znik
5
найти . -тип f | вырезать -d / -f2 | сортировать | uniq -c | sort -nr # исправляет проблемы, упомянутые десертом
jcomeau_ictx
29
find . -type f | cut -d/ -f2 | sort | uniq -c
  • find. -type f найти все элементы типа файла
  • cut -d/ -f2 чтобы вырезать их конкретную папку
  • sort отсортировать список имен папок
  • uniq -c чтобы вернуть количество раз, когда каждое имя папки было подсчитано
DCZ
источник
8
Это намного лучше, чем принятый ответ, поскольку вы получаете сводку каталогов верхнего уровня!
Джейсон Флойд
3
Это должен быть принятый ответ. Просто и понятно.
xssChauhan
1
Лучший ответ, который следует принять, - это этот.
loretoparisi
1
Просто, элегантно и идеально подходит для моих нужд.
RichR,
Отлично. И может быть расширен для подсчета по подкаталогам, заменив спецификаторы поля списком спецификаторов полей. Напр .:find . -type f | cut -d/ -f2,3 | sort | uniq -c
algal
16

Вы можете организовать поиск всех файлов, удалить имена файлов, оставив вам строку, содержащую только имя каталога для каждого файла, а затем подсчитать, сколько раз появляется каждый каталог:

find . -type f |
sed 's%/[^/]*$%%' |
sort |
uniq -c

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


Если вас интересует количество файлов в каждом подкаталоге текущего каталога, подсчет любых файлов в любых подкаталогах вместе с файлами в непосредственном подкаталоге, тогда я бы адаптировал sedкоманду только для печати каталог верхнего уровня:

find . -type f |
sed -e 's%^\(\./[^/]*/\).*$%\1%' -e 's%^\.\/[^/]*$%./%' |
sort |
uniq -c

Первый шаблон фиксирует начало имени, точку, косую черту, имя до следующей косой черты и косую черту и заменяет строку только первой частью, поэтому:

./dir1/dir2/file1

заменяется на

./dir1/

Вторая замена захватывает файлы непосредственно в текущем каталоге; у них нет косой черты в конце, и они заменяются на ./. Затем сортировка и подсчет работают только с количеством имен.

Джонатан Леффлер
источник
1
Это не выводит имена каталогов, которые не содержат файлов. Не уверен, требуется ли это.
Остин Филлипс
Правда, это не так. Исправить это не так уж и легко, поскольку пустые имена каталогов даже не могут появиться в выводе find. Некоторые могут: если файл есть dir1/dir2/dir3/file1, но dir1/dir2содержит только подкаталоги (без простых файлов), вы можете сделать вывод о его наличии. Но если dir1/dir4файлов нет, его имя просто не появляется.
Джонатан Леффлер,
Очень полезный ответ, если вы просто хотите увидеть подкаталоги текущего каталога.
xixixao
Просто зашла поблагодарить. Через 3 года после того, как это было опубликовано, я хотел подсчитать папки 2-го уровня на папку. Ваш пост сэкономил мне потенциально много часов возни с sed, find и неизвестно чем еще
Корвин
13

Вот один из способов сделать это, но, вероятно, не самый эффективный.

find -type d -print0 | xargs -0 -n1 bash -c 'echo -n "$1:"; ls -1 "$1" | wc -l' --

Дает такой вывод с именем каталога, за которым следует количество записей в этом каталоге. Обратите внимание, что счетчик вывода также будет включать записи каталога, которые могут быть не такими, как вы хотите.

./c/fa/l:0
./a:4
./a/c:0
./a/a:1
./a/a/b:0
Остин Филлипс
источник
Это кажется очень дорогим для запуска 3 команды ( bash, ls, wc) для каждого каталога , найденного find.
Джонатан Леффлер
@JonathanLeffler Согласен, отсюда первая строка моего ответа. Ваше решение лучше.
Остин Филлипс
круто, это то, что я ищу, могу я спросить, что такое "-" в конце?
один раз,
1
@once - принадлежит команде bash, которая будет порождена xargs. С man bash, A -- signals the end of options and disables further option processing. В этом случае это предотвратит включение неверно названного файла, найденного как часть поиска, в обработку аргументов для bash.
Остин Филлипс
8

У всех остальных есть тот или иной недостаток.

find -type d -readable -exec sh -c 'printf "%s " "$1"; ls -1UA "$1" | wc -l' sh {} ';'

Пояснение:

  • -type d: нас интересуют каталоги.
  • -readable: Они нужны нам только в том случае, если есть возможность перечислить в них файлы. Обратите внимание, что findпри попытке поиска дополнительных каталогов в них по-прежнему будет выдаваться ошибка, но это предотвращает их вызов -exec.
  • -exec sh -c BLAH sh {} ';': для каждого каталога запустите этот фрагмент сценария, $0задав значение shи $1имя файла.
  • printf "%s " "$1": переносимо и минимально вывести имя каталога, за которым следует только пробел, а не новая строка.
  • ls -1UA: перечислить файлы, по одному в строке, в порядке каталогов (чтобы избежать остановки канала), исключая только специальные каталоги .и..
  • wc -l: подсчитать строки
o11c
источник
1
Модификация, чтобы показывать количество файлов первыми в строке и сортировать по ним:find -type d -readable -exec sh -c 'ls -1UA "$1" | wc -l | tr -d "\n" ; printf "\t%s\n" "$1" ' sh {} ';' | sort -n
Евгений Сергеев
он выполняет оболочку много раз, тогда он медленный и сильно использует ресурсы.
Znik
6

Слегка измененная версия ответа Себастьяна с использованием findвместо du(для исключения накладных расходов, связанных с размером файла, которые duдолжны выполняться и никогда не используются):

 find ./ -mindepth 2 -type f | cut -d/ -f2 | sort | uniq -c | sort -nr

-mindepth 2Параметр используется для исключения файлов в текущем каталоге. Если вы удалите его, вы увидите несколько строк вроде следующего:

  234 dir1
  123 dir2
    1 file1
    1 file2
    1 file3
      ...
    1 fileN

(как duвариант на основе)

Если вам нужно также подсчитать файлы в текущем каталоге, используйте эту расширенную версию:

{ find ./ -mindepth 2 -type f | cut -d/ -f2 | sort && find ./ -maxdepth 1 -type f | cut -d/ -f1; } | uniq -c | sort -nr

Результат будет таким:

  234 dir1
  123 dir2
   42 .
Йори Н.
источник
5

Это также можно сделать с помощью цикла по ls вместо find

for f in */; do echo "$f -> $(ls $f | wc -l)"; done

Пояснение:

for f in */; - перебрать все каталоги

do echo "$f -> - распечатать имя каждого каталога

$(ls $f | wc -l) - вызвать ls для этого каталога и подсчитать строки

Sixhobbits
источник
1
Это не работает должным образом, если имена каталогов содержат пробелы.
ксилол
Пытаться for f ./* ; do echo $f $(ls "$f" | wc -l); done
4ndt3s
3

Это должно вернуть имя каталога, за которым следует количество файлов в каталоге.

findfiles() {
    echo "$1" $(find "$1" -maxdepth 1 -type f | wc -l)
}

export -f findfiles

find ./ -type d -exec bash -c 'findfiles "$0"' {} \;

Пример вывода:

./ 6
./foo 1
./foo/bar 2
./foo/bar/bazzz 0
./foo/bar/baz 4
./src 4

В export -fТребуется , поскольку -execаргумент findне позволяет не выполняет функцию Баша , если вы вызываете Баш явно, и вам нужно экспортировать функцию , определенную в текущей области в новую оболочку в явном виде.

Tuxdude
источник
Это кажется чрезмерно сложным. Мне также кажется, что он дает кумулятивные подсчеты для иерархии каталогов, например ./dir1/dir2/dir3(подсчет файлов в dir1и его подкаталогов вместе, а не подсчет файлов dir1/dir2/dir3отдельно от тех, которые находятся в, dir1/dir2и обоих отдельно от тех, в /dir1).
Джонатан Леффлер
Я понял, что этого хотел автор. Если это не так, то я согласен, что ответ не имеет отношения к вопросу.
Tuxdude 05
1
@JonathanLeffler - Хорошо, прочитав вопрос еще раз, я понял, что вы правы - изменил ответ соответствующим образом.
Tuxdude 05
2

Я объединил ответ @glenn jackman и ответ @ pcarvalho (в списке комментариев что-то не так с ответом pcarvalho, потому что дополнительная функция управления стилем персонажа ' ' '( )).

Мой скрипт может принимать путь в качестве дополнения и сортировать список каталогов по нему, а ls -lтакже может обрабатывать проблему «места в имени файла» .

#!/bin/bash
OLD_IFS="$IFS"
IFS=$'\n'
for dir in $(find $1 -maxdepth 1 -type d | sort); 
do
    files=("$dir"/*)
    printf "%5d,%s\n" "${#files[@]}" "$dir"
done
FS="$OLD_IFS"

Мой первый ответ в stackoverflow, и я надеюсь, что это поможет кому-то ^ _ ^

отпуск
источник
1

найти . -тип f -printf '% h \ n' | сортировать | uniq -c

дает, например:

  5 .
  4 ./aln
  5 ./aln/iq
  4 ./bs
  4 ./ft
  6 ./hot
Головокружение
источник
0

Я пробовал с некоторыми другими здесь, но в итоге в подсчет файлов были включены подпапки, когда мне нужны были только файлы. Будет напечатано ./folder/path<tab>nnnколичество файлов, не включая вложенные папки, для каждой вложенной папки в текущей папке.

for d in `find . -type d -print` 
do 
  echo -e "$d\t$(find $d -maxdepth 1 -type f -print | wc -l)"
done
sthames42
источник
0

Простой способ рекурсивного поиска файлов заданного типа. В этом случае файлы .jpg для всех папок в текущем каталоге:

find . -name *.jpg -print | wc -l

РексБаркер
источник
0

Сверхбыстрая чудо-команда, которая рекурсивно просматривает файлы, чтобы подсчитать количество изображений в каталоге и организовать вывод по расширению изображения:

find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'

Кредиты: https://unix.stackexchange.com/a/386135/354980

tsveti_iko
источник
0

Это мог бы быть еще один способ просмотра структур каталогов и получения результатов глубины.

find . -type d  | awk '{print "echo -n \""$0"  \";ls -l "$0" | grep -v total | wc -l" }' | sh 
Джозеф Эрнест
источник
0

Я отредактировал скрипт, чтобы исключить все node_modulesкаталоги внутри анализируемого.

Это можно использовать, чтобы проверить, не превышает ли количество файлов проекта максимальное количество файлов, которое может обработать средство отслеживания файлов.

find . -type d ! -path "*node_modules*" -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

Чтобы проверить максимальное количество файлов, которые может просматривать ваша система:

cat /proc/sys/fs/inotify/max_user_watches

node_modules Папка должна быть добавлена ​​к исключенным путям среды IDE / редактора в медленных системах, а количество других файлов в идеале не должно превышать максимальное значение (которое можно изменить).

Спонсор
источник
-1

Это даст общий счет.

for file in */; do echo "$file -> $(ls $file | wc -l)"; done | cut -d ' ' -f 3| py --ji -l 'numpy.sum(l)'
Нага Венкатеш Гавини
источник
Нет, не будет. Он будет рассматривать только один уровень подкаталогов.
Кусалананда