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

141

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

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

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

mbaitoff
источник

Ответы:

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

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

SHW
источник
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
Ян Chren - Rindeal
источник
Мне нравится простота этого ответа, хотя он работал только для меня, когда я вводил пробелы после открывающей скобки и перед закрывающей скобкой. Я действительно задаюсь вопросом, будет ли он действительно поддерживать «бесконечное» количество файлов :)
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"'

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

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

LC_ALL=C find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
LBO
источник
1

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

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

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

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