Mengapa Anda ingin menggunakan findsaat Bash melakukannya? (shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done): untuk semua direktori, hitung jumlah entri dalam direktori tersebut (termasuk file titik tersembunyi, tidak termasuk .dan ..)
janmoesen
@janmoesen Mengapa Anda tidak menjawabnya? Saya baru mengenal shell scripting, tetapi saya tidak dapat melihat gotcha dengan metode Anda. Bagi saya, sepertinya cara terbaik. Tidak ada yang mengunggulkan komentar Anda, tetapi tidak ada yang mengomentari mengapa itu mungkin buruk juga. Jawaban yang tervvvatif memiliki lebih banyak perwakilan daripada Anda sehingga membuat saya bertanya-tanya apakah saya kehilangan sesuatu.
toxalot
@toxalot: Saya tidak repot-repot menambahkannya sebagai jawaban karena terlalu pendek (dan mungkin agak merendahkan nada). Jangan ragu untuk mengunggah komentar. :-) Juga, pertanyaannya agak kabur sehubungan dengan apa "berapa banyak file" artinya. Solusi saya menghitung file dan direktori "biasa" ; mungkin poster itu benar-benar berarti "file, bukan direktori". Satu hal yang perlu diingat adalah bahwa globbing ini tidak memperhitungkan file dot "tersembunyi". Ada beberapa cara untuk mengatasi kedua gotcha itu. Tetapi sekali lagi: tidak yakin tentang persyaratan pasti dari poster asli.
janmoesen
Jawaban:
30
Ini melakukannya dengan cara yang aman dan portabel. Itu tidak akan bingung dengan nama file yang aneh.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Perhatikan bahwa ini akan mencetak jumlah file terlebih dahulu, kemudian nama direktori pada baris terpisah. Jika Anda ingin mempertahankan format OP, Anda perlu pemformatan lebih lanjut, mis
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Jika Anda memiliki kumpulan subdirektori tertentu yang Anda minati, Anda dapat menggantinya *dengan itu.
Kenapa ini aman? (dan karenanya layak menggunakan skrip)
Nama file dapat berisi karakter apa pun kecuali /. Ada beberapa karakter yang diperlakukan secara khusus oleh shell atau oleh perintah. Itu termasuk spasi, baris baru, dan garis putus-putus.
Menggunakan for f in *konstruk adalah cara aman untuk mendapatkan setiap nama file, apa pun isinya.
Setelah Anda memiliki nama file dalam variabel, Anda masih harus menghindari hal-hal seperti find $f. Jika $fberisi nama file -test, findakan mengeluh tentang opsi yang baru saja Anda berikan. Cara untuk menghindari itu adalah dengan menggunakan ./di depan nama; cara ini memiliki arti yang sama, tetapi tidak lagi dimulai dengan tanda hubung.
Baris baru dan spasi juga menjadi masalah. Jika $fberisi "halo, teman" sebagai nama file find ./$f,, adalah find ./hello, buddy. Anda mengatakan finduntuk melihat ./hello,dan buddy. Jika itu tidak ada, itu akan mengeluh, dan itu tidak akan pernah masuk ./hello, buddy. Ini mudah dihindari - gunakan tanda kutip di sekitar variabel Anda.
Akhirnya, nama file dapat berisi baris baru, jadi menghitung baris baru dalam daftar nama file tidak akan berfungsi; Anda akan mendapatkan jumlah tambahan untuk setiap nama file dengan baris baru. Untuk menghindarinya, jangan hitung baris baru dalam daftar file; alih-alih, hitung baris baru (atau karakter lain) yang mewakili satu file. Inilah sebabnya mengapa findperintahnya sederhana -exec echo \;dan tidak -exec echo {} \;. Saya hanya ingin mencetak satu baris baru untuk tujuan penghitungan file.
Hitungannya akan termasuk direktori itu sendiri. Jika Anda ingin mengecualikan itu dari hitungan, gunakan-mindepth 1
toxalot
Anda juga bisa menggunakan -printf '\n'bukan -exec echo.
toxalot
1
@toxalot Anda bisa jika Anda memiliki temuan yang mendukung -printf, tetapi tidak jika Anda ingin itu berfungsi di FreeBSD, misalnya.
Shawn J. Goff
6
Dengan asumsi Anda mencari solusi Linux standar, cara yang relatif mudah untuk mencapai ini adalah dengan find:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Di mana findmelintasi dua subdirektori yang ditentukan, ke a -maxdepthdari 1 yang mencegah rekursi lebih lanjut dan hanya melaporkan file ( -type f) yang dipisahkan oleh baris baru. Hasilnya kemudian disalurkan wcuntuk menghitung jumlah garis-garis itu.
Saya memiliki lebih dari 2 dir ... Bagaimana saya bisa menggabungkan perintah Anda dengan find . -maxdepth 1 -type doutput?
ShyBoy
Anda dapat (a) memasukkan direktori yang diperlukan dalam sebuah variabel dan find $dirs ...atau, (b) jika mereka secara eksklusif berada di satu direktori tingkat yang lebih tinggi, glob dari direktori itu,find */ ...
jasonwryan
1
Ini akan melaporkan hasil yang salah jika ada nama file yang memiliki karakter baris baru di dalamnya.
Shawn J. Goff
@ Shawn: terima kasih. Saya pikir saya memiliki nama file dengan spasi tertutup, tetapi tidak mempertimbangkan baris baru: ada saran untuk perbaikan?
jasonwryan
Tambahkan -exec echoke perintah find Anda - dengan cara itu tidak menggemakan nama file, hanya baris baru.
Shawn J. Goff
5
Dengan “tanpa rekursi”, apakah maksud Anda jika directoryName1memiliki subdirektori, maka Anda tidak ingin menghitung file dalam subdirektori? Jika demikian, inilah cara untuk menghitung semua file biasa di direktori yang ditunjukkan:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
Perhatikan bahwa -ftes ini melakukan dua fungsi: tes apakah entri yang cocok dengan salah satu gumpalan di atas adalah file biasa, dan menguji apakah entri itu cocok (jika salah satu gumpalan tidak cocok, pola tetap seperti apa adanya¹). Jika Anda ingin menghitung semua entri dalam direktori yang diberikan terlepas dari jenisnya, ganti -fdengan -e.
Ksh memiliki cara untuk membuat pola yang cocok dengan file titik dan untuk menghasilkan daftar kosong jika tidak ada file yang cocok dengan pola. Jadi di ksh Anda dapat menghitung file biasa seperti ini:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
¹ Perhatikan bahwa setiap pola cocok dengan dirinya sendiri, jika tidak hasilnya akan mati (mis. Jika Anda menghitung file yang dimulai dengan angka, Anda tidak bisa hanya melakukannya for x in [0-9]*; do if [ -f "$x" ]; then …karena mungkin ada file yang dipanggil [0-9]foo).
Berdasarkan skrip penghitungan , jawaban Shawn dan trik Bash untuk memastikan bahkan nama file dengan baris baru dicetak dalam bentuk yang dapat digunakan pada satu baris:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %qadalah untuk mencetak versi string yang dikutip, yaitu string satu baris yang dapat Anda masukkan ke dalam skrip Bash untuk ditafsirkan sebagai string literal termasuk (berpotensi) baris baru dan karakter khusus lainnya. Sebagai contoh, lihat echo -n $'\tfoo\nbar'vs printf %q $'\tfoo\nbar'.
The findperintah bekerja dengan hanya mencetak satu karakter untuk setiap file, dan kemudian menghitung orang-orang bukan garis penghitungan.
Selamat datang di U&L. Jawaban harus berbentuk panjang dengan penjelasan dan bukan hanya kode tetes. Harap perluas ini dan jelaskan apa yang terjadi. Juga ini adalah cara yang sangat tidak efisien untuk melakukan ini dan tidak menangani file dengan spasi, misalnya.
slm
-1
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
saat Bash melakukannya?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: untuk semua direktori, hitung jumlah entri dalam direktori tersebut (termasuk file titik tersembunyi, tidak termasuk.
dan..
)Jawaban:
Ini melakukannya dengan cara yang aman dan portabel. Itu tidak akan bingung dengan nama file yang aneh.
Perhatikan bahwa ini akan mencetak jumlah file terlebih dahulu, kemudian nama direktori pada baris terpisah. Jika Anda ingin mempertahankan format OP, Anda perlu pemformatan lebih lanjut, mis
Jika Anda memiliki kumpulan subdirektori tertentu yang Anda minati, Anda dapat menggantinya
*
dengan itu.Kenapa ini aman? (dan karenanya layak menggunakan skrip)
Nama file dapat berisi karakter apa pun kecuali
/
. Ada beberapa karakter yang diperlakukan secara khusus oleh shell atau oleh perintah. Itu termasuk spasi, baris baru, dan garis putus-putus.Menggunakan
for f in *
konstruk adalah cara aman untuk mendapatkan setiap nama file, apa pun isinya.Setelah Anda memiliki nama file dalam variabel, Anda masih harus menghindari hal-hal seperti
find $f
. Jika$f
berisi nama file-test
,find
akan mengeluh tentang opsi yang baru saja Anda berikan. Cara untuk menghindari itu adalah dengan menggunakan./
di depan nama; cara ini memiliki arti yang sama, tetapi tidak lagi dimulai dengan tanda hubung.Baris baru dan spasi juga menjadi masalah. Jika
$f
berisi "halo, teman" sebagai nama filefind ./$f
,, adalahfind ./hello, buddy
. Anda mengatakanfind
untuk melihat./hello,
danbuddy
. Jika itu tidak ada, itu akan mengeluh, dan itu tidak akan pernah masuk./hello, buddy
. Ini mudah dihindari - gunakan tanda kutip di sekitar variabel Anda.Akhirnya, nama file dapat berisi baris baru, jadi menghitung baris baru dalam daftar nama file tidak akan berfungsi; Anda akan mendapatkan jumlah tambahan untuk setiap nama file dengan baris baru. Untuk menghindarinya, jangan hitung baris baru dalam daftar file; alih-alih, hitung baris baru (atau karakter lain) yang mewakili satu file. Inilah sebabnya mengapa
find
perintahnya sederhana-exec echo \;
dan tidak-exec echo {} \;
. Saya hanya ingin mencetak satu baris baru untuk tujuan penghitungan file.sumber
-mindepth 1
-printf '\n'
bukan-exec echo
.-printf
, tetapi tidak jika Anda ingin itu berfungsi di FreeBSD, misalnya.Dengan asumsi Anda mencari solusi Linux standar, cara yang relatif mudah untuk mencapai ini adalah dengan
find
:Di mana
find
melintasi dua subdirektori yang ditentukan, ke a-maxdepth
dari 1 yang mencegah rekursi lebih lanjut dan hanya melaporkan file (-type f
) yang dipisahkan oleh baris baru. Hasilnya kemudian disalurkanwc
untuk menghitung jumlah garis-garis itu.sumber
find . -maxdepth 1 -type d
output?find $dirs ...
atau, (b) jika mereka secara eksklusif berada di satu direktori tingkat yang lebih tinggi, glob dari direktori itu,find */ ...
-exec echo
ke perintah find Anda - dengan cara itu tidak menggemakan nama file, hanya baris baru.Dengan “tanpa rekursi”, apakah maksud Anda jika
directoryName1
memiliki subdirektori, maka Anda tidak ingin menghitung file dalam subdirektori? Jika demikian, inilah cara untuk menghitung semua file biasa di direktori yang ditunjukkan:Perhatikan bahwa
-f
tes ini melakukan dua fungsi: tes apakah entri yang cocok dengan salah satu gumpalan di atas adalah file biasa, dan menguji apakah entri itu cocok (jika salah satu gumpalan tidak cocok, pola tetap seperti apa adanya¹). Jika Anda ingin menghitung semua entri dalam direktori yang diberikan terlepas dari jenisnya, ganti-f
dengan-e
.Ksh memiliki cara untuk membuat pola yang cocok dengan file titik dan untuk menghasilkan daftar kosong jika tidak ada file yang cocok dengan pola. Jadi di ksh Anda dapat menghitung file biasa seperti ini:
atau semua file seperti ini:
Bash memiliki berbagai cara untuk membuatnya lebih sederhana. Untuk menghitung file biasa:
Untuk menghitung semua file:
Seperti biasa, ini bahkan lebih sederhana di zsh. Untuk menghitung file biasa:
Ubah
(DN.)
ke(DN)
untuk menghitung semua file.¹ Perhatikan bahwa setiap pola cocok dengan dirinya sendiri, jika tidak hasilnya akan mati (mis. Jika Anda menghitung file yang dimulai dengan angka, Anda tidak bisa hanya melakukannya
for x in [0-9]*; do if [ -f "$x" ]; then …
karena mungkin ada file yang dipanggil[0-9]foo
).sumber
Berdasarkan skrip penghitungan , jawaban Shawn dan trik Bash untuk memastikan bahkan nama file dengan baris baru dicetak dalam bentuk yang dapat digunakan pada satu baris:
printf %q
adalah untuk mencetak versi string yang dikutip, yaitu string satu baris yang dapat Anda masukkan ke dalam skrip Bash untuk ditafsirkan sebagai string literal termasuk (berpotensi) baris baru dan karakter khusus lainnya. Sebagai contoh, lihatecho -n $'\tfoo\nbar'
vsprintf %q $'\tfoo\nbar'
.The
find
perintah bekerja dengan hanya mencetak satu karakter untuk setiap file, dan kemudian menghitung orang-orang bukan garis penghitungan.sumber
Berikut adalah "brute-force" cara ish untuk mendapatkan hasil Anda, menggunakan
find
,echo
,ls
,wc
,xargs
danawk
.sumber
sumber
sumber