Saya perlu memeriksa semua sub-direktori dan melaporkan berapa banyak file (tanpa rekursi lebih lanjut) yang dikandungnya:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Saya perlu memeriksa semua sub-direktori dan melaporkan berapa banyak file (tanpa rekursi lebih lanjut) yang dikandungnya:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Jawaban:
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 $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 file find ./$f
,, adalah find ./hello, buddy
. Anda mengatakan find
untuk 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 find
perintahnya sederhana -exec echo \;
dan tidak -exec echo {} \;
. Saya hanya ingin mencetak satu baris baru untuk tujuan penghitungan file.
-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
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
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 disalurkan wc
untuk menghitung jumlah garis-garis itu.
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:
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 -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:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
atau semua file seperti ini:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash memiliki berbagai cara untuk membuatnya lebih sederhana. Untuk menghitung file biasa:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Untuk menghitung semua file:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Seperti biasa, ini bahkan lebih sederhana di zsh. Untuk menghitung file biasa:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
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
).
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 %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, lihat echo -n $'\tfoo\nbar'
vs printf %q $'\tfoo\nbar'
.
The find
perintah bekerja dengan hanya mencetak satu karakter untuk setiap file, dan kemudian menghitung orang-orang bukan garis penghitungan.
Berikut adalah "brute-force" cara ish untuk mendapatkan hasil Anda, menggunakan find
, echo
, ls
, wc
, xargs
dan awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
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..
)