Вопрос или проблема
Предположим, что есть каталог для хранения изображений, скажем, ./photos/john_doe
, в котором есть несколько подкаталогов, где находятся много определенных файлов (например, *.jpg
). Как я могу рассчитать общую размерность этих файлов в каталоге john_doe
?
Я пробовал du -hs ./photos/john_doe/*/*.jpg
, но это показывает только индивидуальные файлы. Также это отслеживает только первый уровень вложенности в каталоге john_doe
, как john_doe/june/
, но пропускает john_doe/june/outrageous/
.
Итак, как я могу обойти весь каталог, суммируя размер определенных файлов?
find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Если требуется более одного вызова du
, потому что список файлов слишком длинный, будет сообщено несколько итоговых значений, которые нужно будет суммировать.
du -ch public_html/images/*.jpg | grep total
20M total
дает мне общий объем использования моих .jpg
файлов в этом каталоге.
Чтобы справиться с несколькими каталогами, вам, вероятно, придется как-то объединить это с find
.
Вы можете найти примеры команды du полезными (также включает find
)
В первую очередь, вам нужно две вещи:
- опция
-c
дляdu
, чтобы сказать ему, что нужно получить общий итог; - либо
**
(инструкции по активации), либоfind
(пример), чтобы проходить по подкаталогам.
du -ch -- **/*.jpg | tail -n 1
Если вы ищете эффективный способ суммирования размеров файлов в каталоге, вот окончательный ответ:
find <DIR> -type f -name "*.<EXT>" -printf "%s\n" | gawk -M '{t+=$1}END{print t}'
Если у вас нет GNU AWK с поддержкой больших чисел, используйте эту версию с утилитой bc
. Это немного медленнее и ограничено объемом оперативной памяти при подсчете миллиардов файлов.
{ find <DIR> -type f -name "*.<EXT>" -printf "%s+"; echo 0; } | bc
Читаемый размер
Чтобы преобразовать выводимый размер в байтах в читаемый формат, например, 176.7M, просто добавьте:
| numfmt --to=si
Почему это решение выделяется:
-
Универсальный выбор файлов: Используйте все возможности
find
, чтобы точно указать, какие файлы вас интересуют. -
Работа с большими объемами: Поддерживает миллиарды файлов, в отличие от других решений, ограниченных максимальной длиной списка аргументов.
-
Эффективное управление процессами: Запускает только 3 простых процесса с минимальной пропускной способностью, в отличие от других методов, которые запускают C+N процессов (где C — это константа, а N — количество файлов).
-
Без проблем с манипуляцией строками: Этот подход избегает ненужной манипуляции строками, grep или regex. Команда
find
справляется со всем, что вам когда-либо потребуется, нативно и надежно.
Ответы, данные до сих пор, не учитывают, что список файлов, переданный от find в du, может быть таким длинным, что find автоматически разбивает список на части, в результате чего появляется несколько вхождений total
.
Вы можете либо grep total
(локаль!) и суммировать вручную, либо использовать другую команду. Насколько я знаю, есть только два способа получить общий итог (в килобайтах) всех файлов, найденных с помощью 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=-
Если список файлов слишком велик, чтобы его можно было передать в один вызов 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
. Размер будет выражен в байтах.
Упомянутые до сих пор решения неэффективны (exec дорог), а также требуют дополнительной ручной работы для суммирования, если список файлов длинный, или они не работают на Mac OS X. Следующее решение очень быстрое, должно работать на любой системе и дает общий ответ в ГБ (удалите /1024, если хотите увидеть общее в МБ):
find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'
Улучшение отличного ответа SHW, чтобы он работал с любым языком, как уже упоминал Збысек в своем комментарии:
LC_ALL=C find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
du естественно проходит по иерархии каталогов, а awk может выполнять фильтрацию, поэтому что-то вроде этого может быть достаточным:
du -ak | awk 'BEGIN {sum=0} /\.jpg$/ {sum+=$1} END {print sum}'
Это работает без GNU.
Вот что сработало для меня.
find -type f -iname *.jpg -print0 | du -ch --files0-from=- | grep total$
Используя современный fd
(также известный как fd-find
или fdfind
на Ubuntu)
fdfind -e jpg -X du -ch | tail -1
Мне показалось, что fd
проще в использовании, чем find
, и нет необходимости включать globstar
Трюк заключается в использовании заглавной X
–exec-batch, которая выполняет команду только один раз, а не строчной x
, которая выполняет обычный exec
для каждого файла.
Чтобы установить на Ubuntu:
sudo apt install fd-find
Другой вариант был бы
ls -al <directory> | awk '{t+=$5}END{print t}}'
Предполагая, что вы смотрите в одном каталоге. Если вы хотите посмотреть в текущем каталоге и ниже:
ls -Ral <directory> | awk '{t+=$5}END{print t}}'
Другой альтернативный вариант — использовать stat вместо du
stat -L -c %s ** | awk '{s+=$1} END {printf "%.0f\n", s}'
Смотрите ответ Жиля о использовании **
Это смешение нескольких ответов и комментариев, которые делают то, что мне нужно.
find . \( -iname "*.jpg" -o -iname "*.png" \) -type f -exec du -bc {} + | grep total$ | cut -f1 | awk '{ total += $1 }; END { print total }'| numfmt --to=iec
find
найдет все файлы рекурсивно-iname
используется для нечувствительности к регистру-o
и круглые скобки для поиска по нескольким паттернамdu -bc
получит размер файлов, иногда с более чем одним вызовом, если файлов многоgrep total
выведет только строкуtotal
, как указано вdu
cut -f1
возьмет только фактические целые значенияawk
суммирует их всеnumfmt
преобразует его в читаемый формат
Ответ или решение
Для того чтобы вычислить общую размерность определённых файлов (например, изображений формата *.jpg) в директории, содержащей подкаталоги, можно воспользоваться утилитами командной строки, такими как find
и du
.
Полное решение задачи:
-
Использование команды
find
сdu
для суммирования файлов:
Вы можете воспользоваться следующей командой:find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Эта команда находит все файлы с расширением
.jpg
в директорииjohn_doe
и всех её подкаталогах и передаёт их вdu
, который суммирует размеры файлов.grep total$
фильтрует вывод, оставляя только строку с итоговым размером. Однако стоит учитывать, что если количество файлов будет очень большим, возможно, придется выполнить несколько вызововdu
, что может привести к множественным выводам. -
Альтернативное решение с
awk
илиbc
:
Если вы хотите избежать проблемы с несколькими вызовамиdu
, можно воспользоваться следующим способом:find ./photos/john_doe -type f -name '*.jpg' -print0 | du -ch --files0-from=- | grep total$
В этом случае используется
-print0
для вывода списка файлов с нулями (\0
) в качестве разделителей. Это позволяет избежать проблем с пробелами в именах файлов. -
Посчитать размер в байтах:
Если вы хотите получить общий размер в байтах и преобразовать его в удобочитаемый формат, используйтеawk
:find ./photos/john_doe -type f -name '*.jpg' -printf "%s\n" | awk '{total += $1} END {print total, "bytes"}'
Эта команда находит все файлы с расширением
.jpg
, выводит их размер в байтах, и с помощьюawk
суммирует их размеры. -
Для вывода в удобочитаемом формате:
Чтобы вывести общий размер в удобочитаемом формате (например, МБ или ГБ), можно добавитьnumfmt
:find ./photos/john_doe -type f -name '*.jpg' -printf "%s\n" | awk '{total += $1} END {print total}' | numfmt --to=iec
Заключение
Вышеуказанные команды обеспечивают эффективный способ подсчета общего размера определённых файлов в каталоге и всех его подкаталогах. Каждый метод имеет свои преимущества, и выбор зависит от ваших предпочтений и требований к удобочитаемости. Убедитесь, что все команды выполняются в терминале Unix-подобной системы, где установлен необходимый набор утилит.