Temukan ukuran total file tertentu dalam cabang direktori

140

Asumsikan ada direktori penyimpanan gambar, katakanlah,, ./photos/john_doedi dalamnya terdapat beberapa subdirektori, tempat banyak file tertentu berada (misalnya, *.jpg). Bagaimana saya bisa menghitung ukuran ringkasan file-file di bawah john_doecabang?

Saya sudah mencoba du -hs ./photos/john_doe/*/*.jpg, tetapi ini hanya menampilkan file individual. Juga, ini hanya melacak tingkat sarang pertama dari john_doedirektori, seperti john_doe/june/, tetapi melompat john_doe/june/outrageous/.

Jadi, bagaimana saya bisa melintasi seluruh cabang, merangkum ukuran file tertentu?

mbaitoff
sumber

Jawaban:

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

Jika lebih dari satu doa dudiperlukan karena daftar file sangat panjang, beberapa total akan dilaporkan dan perlu dijumlahkan.

SHW
sumber
7
temukan -iname 'file *' -exec du -cb {} + | grep total $ | potong -f1 | rekatkan -sd + - | bc # jumlah byte yang dijumlahkan
Michal Čizmazia
3
Jika sistem Anda bekerja di bawah bahasa lain, maka Anda perlu mengubah total $ ke kata lain seperti razem $ dalam bahasa Polandia.
Zbyszek
1
Anda dapat menambahkan LC_ALL=POSIXsebagai awalan untuk selalu grep total seperti ini:LC_ALL=POSIX find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Sven
2
Jika Anda tidak menggunakan -name, maka ubah grep ke grep -P "\ttotal$"atau yang lain itu akan menangkap semua file yang diakhiri dengan "total" juga.
thdoan
3
@ MichalČizmazia beberapa shell (misalnya, Git Bash untuk Windows) tidak datang bc, jadi di sini adalah solusi yang lebih portabel:find -name '*.jpg' -type f -exec du -bc {} + | grep total$ | cut -f1 | awk '{ total += $1 }; END { print total }'
thdoan
50
du -ch public_html/images/*.jpg | grep total
20M total

memberi saya total penggunaan .jpgfile saya di direktori ini.

Untuk berurusan dengan banyak direktori, Anda mungkin harus menggabungkan ini dengan findentah bagaimana.

Anda mungkin menemukan contoh perintah du berguna (juga termasuk find)

Levon
sumber
2
Ini tidak melintasi direktori yang mendasarinya?
mbaitoff
Ini lebih mudah diketik daripada solusi yang diterima, tetapi hanya setengah benar, ini tidak akan memasukkan gambar dalam subdirektori. Senang mengetahui apakah semua file berada dalam satu direktori.
gbmhunter
@ gbmhunter Saya pikir jika Anda menambahkan parameter -R ke -ch Anda juga akan mendapatkan subdirektori karena secara rekursif melintasi pohon direktori. Saya saat ini tidak di komputer untuk mencobanya untuk mengonfirmasi.
Levon
1
Saya tidak melihat -Ropsi di man7.org/linux/man-pages/man1/du.1.html . Dan saya tidak berpikir opsi rekursif akan membantu dalam kasus ini karena shell melakukan ekspansi glob sebelum meneruskan argumen du.
gbmhunter
22

Terutama, Anda membutuhkan dua hal:

  • yang -cpilihan untuk du, untuk menceritakannya untuk menghasilkan grand total;
  • baik **( instruksi aktivasi ) atau find( contoh ) atau untuk melintasi subdirektori.
du -ch -- **/*.jpg | tail -n 1
Gilles
sumber
balasan yang sangat bagus Lebih sederhana daripada menggunakan find (selama * atau ** cocok dengan struktur direktori)
Andre de Miranda
Itu juga dapat menangani daftar file yang sangat panjang sedangkan menggunakan finddapat mengembalikan hasil yang salah.
Eric Fournie
ekspansi bash brace memungkinkan untuk mengukur beberapa set wildcard juga. du -ch -- ./{dir1,dir2}/*.jpgataudu -ch -- ./{prefix1*,prefix2*}.jpg
J.Money
@EricFournie Namun saya mendapat Argument list too longkesalahan saat memproses sekitar 300 ribu file teks.
xtluo
Jumlah argumen maksimum untuk suatu perintah (dalam hal ini, nama file yang dikembalikan oleh ekspansi wildcard) dapat diperiksa getconf ARG_MAX. Jika Anda memiliki lebih banyak, Anda perlu memproses file satu per satu atau secara bersamaan dengan for for.
Eric Fournie
17

Jawaban akhirnya adalah:

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

dan bahkan versi yang lebih cepat, tidak dibatasi oleh RAM, tetapi itu membutuhkan GNU AWK dengan dukungan bignum:

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

Versi ini memiliki beberapa fitur berikut:

  • semua kemampuan finduntuk menentukan file yang Anda cari
  • mendukung jutaan file
    • jawaban lain di sini dibatasi oleh panjang maksimal daftar argumen
  • memunculkan hanya 3 proses sederhana dengan throughput pipa minimal
    • banyak jawaban di sini menelurkan proses C + N, di mana C adalah beberapa konstan dan N adalah jumlah file
  • tidak peduli dengan manipulasi string
    • versi ini tidak melakukan grepping, atau regexing
    • baik, findapakah pencocokan wildcard sederhana dari nama file
  • opsional format jumlah menjadi bentuk yang dapat dibaca manusia (misalnya. 5.5K, 176.7M, ...)
    • untuk melakukan penambahan itu | numfmt --to=si
Jan Chren - rindeal
sumber
Saya suka kesederhanaan dari jawaban ini, meskipun itu hanya bekerja untuk saya ketika saya memperkenalkan spasi setelah penjepit pembuka dan sebelum penjepit penutup. Saya bertanya-tanya apakah itu benar-benar akan mendukung sejumlah file 'infiinte' :)
andyb
1
@andyb terima kasih atas umpan baliknya, ruang di sekitar kawat gigi memang diperlukan di BASH, saya menggunakan ZSH jadi saya tidak memperhatikan itu. Dan jumlah file dibatasi oleh RAM yang tersedia di sistem Anda karena penggunaan memori bc tumbuh perlahan-lahan ketika angka-angka mengalir masuk.
Jan Chren - rindeal
8

Jawaban yang diberikan sampai sekarang tidak memperhitungkan bahwa daftar file yang diteruskan dari find ke du mungkin begitu lama sehingga menemukan secara otomatis membagi daftar menjadi potongan-potongan, yang mengakibatkan beberapa kejadian total.

Anda dapat grep total(lokal!) Dan meringkas secara manual, atau menggunakan perintah yang berbeda. AFAIK hanya ada dua cara untuk mendapatkan jumlah total (dalam kilobyte) dari semua file yang ditemukan oleh find:
find . -type f -iname '*.jpg' -print0 | xargs -r0 du -a| awk '{sum+=$1} END {print sum}'

Penjelasan
find . -type f -iname '*.jpg' -print0: Temukan semua file dengan ekstensi jpg terlepas dari huruf besar-kecil (mis. * .Jpg, * .JPG, * .Jpg ...) dan hasilkan (diakhiri dengan nol).
xargs -r0 du -a: -r: Xargs akan memanggil perintah bahkan tanpa argumen yang dilewati, yang -r mencegah. -0 berarti string yang diakhiri dengan nol (bukan diakhiri baris baru).
awk '{sum+=$1} END {print sum}': Jumlahkan ukuran file output dengan perintah sebelumnya

Dan untuk referensi, sebaliknya
find . -type f -iname '*.jpg' -print0 | du -c --files0-from=-

Jan
sumber
Petunjuk tambahan: Pada HDD saya dengan 23428 file (22.323 menjadi gambar) metode pertama berjalan 1 detik sementara yang kedua berjalan 3,8 detik.
Jan
Perhatikan bahwa keduanya menganggap sistem GNU. Yang pertama mengasumsikan nama file tidak mengandung karakter baris baru.
Stéphane Chazelas
Saya berani bertaruh du --file0-frombutuh waktu lebih lama karena Anda menjalankannya lebih dulu (efek caching).
Stéphane Chazelas
Dengan xargs, beberapa du -amungkin dijalankan, sehingga Anda mungkin memiliki perbedaan jika ada tautan keras.
Stéphane Chazelas
3

Jika daftar file terlalu besar sehingga tidak dapat diteruskan ke satu permohonan tunggal du -c, pada sistem GNU, Anda dapat melakukan:

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

(ukuran dinyatakan dalam jumlah blok 512 byte). Seperti duitu mencoba menghitung tautan keras hanya sekali. Jika Anda tidak peduli dengan hardlink, Anda dapat menyederhanakannya menjadi:

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

Jika Anda menginginkan ukuran alih-alih penggunaan disk, ganti %bdengan %s. Ukurannya kemudian akan dinyatakan dalam byte.

Stéphane Chazelas
sumber
-bash: bc: command not foundCentos - Linux 2.6.32-431.el6.x86_64
yeya
@yeya, sepertinya penyebaran CentOS Anda rusak. bcadalah perintah POSIX non-opsional.
Stéphane Chazelas
1

Solusi yang disebutkan sejauh ini tidak efisien (exec mahal) dan memerlukan kerja manual tambahan untuk menjumlahkan jika daftar file panjang atau mereka tidak bekerja pada Mac OS X. Solusi berikut ini sangat cepat, harus bekerja pada sistem apa pun, dan menghasilkan jawaban total dalam GB (hapus a / 1024 jika Anda ingin melihat total dalam MB): find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'

hobbydad
sumber
Baik -inameatau -lsstandar / portable, sehingga tidak akan bekerja pada sistem apapun baik. Ini juga tidak akan berfungsi dengan baik jika ada nama file atau target symlink yang berisi karakter baris baru.
Stéphane Chazelas
Perhatikan juga bahwa ia memberikan jumlah ukuran file, bukan penggunaan disk mereka. Untuk symlink, ini memberikan ukuran symlink, bukan file yang mereka tuju.
Stéphane Chazelas
1

Meningkatkan jawaban hebat SHW untuk membuatnya berfungsi dengan lokal apa pun, seperti yang sudah ditunjukkan Zbyszek dalam komentarnya:

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

du secara alami melintasi hierarki direktori dan awk dapat melakukan penyaringan sehingga sesuatu seperti ini mungkin cukup:

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

Ini berfungsi tanpa GNU.

GeoffP
sumber
1
Ini lebih mahal karena memerlukan statpanggilan untuk file yang tidak sesuai dengan pola yang dicari.
Law29
Hanya solusi ini yang berfungsi di mac saya.
Matthias M