Найти общий размер определенных файлов в ветви каталога


141

Предположим, есть каталог хранения изображений, скажем, ./photos/john_doeв котором есть несколько подкаталогов, в которых находится много определенных файлов (скажем, *.jpg). Как я могу рассчитать суммарный размер этих файлов под john_doeветкой?

Я пытался du -hs ./photos/john_doe/*/*.jpg, но это показывает только отдельные файлы. Кроме того, это отслеживает только первый уровень вложенности john_doeкаталога, вроде бы john_doe/june/, но пропускает john_doe/june/outrageous/.

Итак, как я могу пройти всю ветку, суммируя размер определенных файлов?

Ответы:


183
find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$

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


7
find -iname 'file *' -exec du -cb {} + | grep всего $ | вырезать -f1 | вставить -sd + - | bc #
— Суммированный

3
Если ваша система работает на другом языке, вам нужно заменить значение $ на другое слово, например, razem $ на польском языке.
— Збышек,

1
Вы можете добавить в LC_ALL=POSIXкачестве префикса, чтобы всегда grep для общего количества, как это:LC_ALL=POSIX find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
— Sven

2
Если вы не используете -name, то измените grep на, grep -P "\ttotal$"иначе он будет захватывать все файлы, заканчивающиеся на «total».
— thdoan

3
@ MichalČizmazia Некоторые оболочки (например, Git Bash для Windows) не входят в bcfind -name '*.jpg' -type f -exec du -bc {} + | grep total$ | cut -f1 | awk '{ total += $1 }; END { print total }'
— комплект

50
du -ch public_html/images/*.jpg | grep total
20M total

дает мне общее использование моих .jpgфайлов в этом каталоге.

Чтобы иметь дело с несколькими каталогами, вам, вероятно, придется как-то комбинировать это find.

Вы можете найти примеры команд du полезными (они также включают в себя find)


2
Это не пересекает основные каталоги?
— mbaitoff

Это легче набрать, чем принятое решение, но только наполовину верно, оно не будет включать изображения в подкаталогах. Полезно знать, все ли файлы находятся в одном каталоге.
— gbmhunter

@gbmhunter Я думаю, что если вы добавите параметр -R в -ch, вы также получите подкаталоги, поскольку они рекурсивно пересекают дерево каталогов. Я в настоящее время не на компьютере, чтобы проверить это, хотя, чтобы подтвердить.
— Левон

1
Я не вижу -Rварианта на man7.org/linux/man-pages/man1/du.1.html . И я не думаю, что рекурсивный вариант поможет в этом случае, потому что оболочка выполняет расширение glob перед передачей аргументов du.
— gbmhunter

22

Прежде всего, вам нужно две вещи:

du -ch -- **/*.jpg | tail -n 1

очень хороший ответ. Проще, чем использовать find (если long * или ** соответствует структуре каталогов)
— Andre de Miranda

Он также может обрабатывать очень длинные списки файлов, тогда как использование findможет возвращать ошибочные результаты.
— Эрик Фурни

Расширение bash brace позволяет измерять несколько наборов подстановочных знаков. du -ch -- ./{dir1,dir2}/*.jpgилиdu -ch -- ./{prefix1*,prefix2*}.jpg
— J.Money

@EricFournie Однако я получил Argument list too longошибку при обработке около 300 тыс. Текстовых файлов.
— xtluo

Максимальное количество аргументов для команды (в этом случае имена файлов, возвращаемые расширением подстановочного знака) можно проверить с помощью getconf ARG_MAX. Если у вас есть больше, вам нужно будет обрабатывать файлы один за другим или периодически с циклом for.
— Эрик Фурни

17

Окончательный ответ:

{ find <DIR> -type f -name "*.<EXT>" -printf "%s+"; echo 0; } | bc

и даже более быстрая версия, не ограниченная оперативной памятью, но требующая GNU AWK с поддержкой bignum:

find <DIR> -type f -name "*.<EXT>" -printf "%s\n" | gawk -M '{t+=$1}END{print t}'

Эта версия имеет следующие особенности:

  • все возможности findдля указания файлов, которые вы ищете
  • поддерживает миллионы файлов
    • другие ответы здесь ограничены максимальной длиной списка аргументов
  • порождает только 3 простых процесса с минимальной пропускной способностью
    • многие ответы здесь порождают процессы C + N, где C - некоторая константа, а N - количество файлов.
  • не беспокоит манипуляциями со строками
    • эта версия не выполняет никаких операций поиска или регулярных выражений
    • ну, findделает простой подстановочный знак соответствия имен файлов
  • необязательно форматирует сумму в удобочитаемый вид (например. 5.5K, 176.7M...)
    • сделать это добавить | numfmt --to=si

Мне нравится простота этого ответа, хотя он работал только для меня, когда я вводил пробелы после открывающей скобки и перед закрывающей скобкой. Я действительно задаюсь вопросом, будет ли он действительно поддерживать «бесконечное» количество файлов :)
— andyb

1
@andyb спасибо за отзыв, пробелы вокруг фигурных скобок действительно нужны в BASH, я использую ZSH, поэтому я этого не заметил. И количество файлов ограничено доступной оперативной памятью в вашей системе, поскольку использование памяти bc медленно растет по мере поступления чисел.
— Ян Шрен - rindeal

8

Ответы, данные до сих пор, не учитывают, что список файлов, передаваемый от find к du, может быть настолько длинным, что find автоматически разбивает список на куски, что приводит к множественным вхождениям total.

Вы можете либо grep total(локаль!) ​​Подвести итоги вручную, либо использовать другую команду. AFAIK Есть только два способа получить общую сумму (в килобайтах) всех файлов, найденных с помощью find:
find . -type f -iname '*.jpg' -print0 | xargs -r0 du -a| awk '{sum+=$1} END {print sum}'

Объяснение
find . -type f -iname '*.jpg' -print0: Найти все файлы с расширением jpg независимо от регистра (т. Е. * .Jpg, * .JPG, * .Jpg ...) и вывести их (с нулевым символом в конце).
xargs -r0 du -a: -r: Xargs будет вызывать команду даже без переданных аргументов, что предотвращает -r. -0 означает строки с нулевым символом в конце (не завершенный символом новой строки).
awk '{sum+=$1} END {print sum}': Суммировать размеры файлов, выведенные предыдущей командой

И для справки, другой способ будет
find . -type f -iname '*.jpg' -print0 | du -c --files0-from=-


Дополнительная подсказка: на моем жестком диске с 23428 файлами (22323 - изображения) первый метод выполняется 1 секунду, а второй - 3,8 секунды.
— января

Обратите внимание, что оба предполагают систему GNU. Первый предполагает, что имена файлов не содержат символов новой строки.
— Стефан Шазелас

Держу пари, что это du --file0-fromзаняло больше времени, потому что вы запустили его первым (эффект кэширования)
— Стефан Шазелас

С помощью может быть запущено xargsнесколько du -a, поэтому могут возникнуть расхождения при наличии жестких ссылок.
— Стефан Шазелас

3

Если список файлов слишком большой, чтобы его нельзя было передать за один вызов du -c, в системе GNU вы можете сделать:

find . -iname '*.jpg' -type f -printf '%b\t%D:%i\n' |
  sort -u | cut -f1 | paste -sd+ - | bc

(размер выражается в количестве 512 байтных блоков). Как будто duон пытается сосчитать жесткие ссылки только один раз. Если вас не интересуют жесткие ссылки, вы можете упростить их до:

(printf 0; find . -iname '*.jpg' -type f -printf +%b) | bc

Если вы хотите использовать размер вместо использования диска, замените его %bна %s. Размер будет выражен в байтах.


-bash: bc: command not foundCentos - Linux 2.6.32-431.el6.x86_64
— Yeya

@yeya, похоже, что ваше развертывание CentOS не работает. bcявляется необязательной командой POSIX
— Стефан Шазелас

1

Упомянутые решения пока неэффективны (exec стоит дорого) и требуют дополнительной ручной работы для суммирования, если список файлов длинный или они не работают в Mac OS X. Следующее решение очень быстрое, должно работать в любой системе, и выдает итоговый ответ в ГБ (удалите / 1024, если вы хотите увидеть сумму в МБ): find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'


Ни то, -inameни другое не -lsявляется стандартным / переносимым, поэтому он не будет работать ни на одной системе . Это также не будет работать должным образом, если есть имена файлов или целевые ссылки, содержащие символы новой строки.
— Стефан Шазелас

Также обратите внимание, что он дает сумму размеров файлов, а не их использование диска. Для символических ссылок он дает размер символических ссылок, а не файлы, на которые они указывают.
— Стефан Шазелас

1

Улучшение SHW - отличный ответ, чтобы он работал с любой локалью, как Zbyszek уже указал в своем комментарии:

LC_ALL=C find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$

1

Естественно, du просматривает иерархию каталогов, и awk может выполнять фильтрацию, поэтому может быть достаточно чего-то подобного:

du -ak | awk 'BEGIN {sum=0} /\.jpg$/ {sum+=$1} END {print sum}'

Это работает без GNU.


1
Это дороже, так как влечет за собой statвызов файлов, которые не соответствуют искомому шаблону.
— Law29

Только это решение работает на моем Mac.
— Матиас М
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.