CentOS 5.9
Saya menemukan masalah beberapa hari yang lalu di mana sebuah direktori memiliki banyak file. Untuk menghitungnya, saya berlarils -l /foo/foo2/ | wc -l
Ternyata ada lebih dari 1 juta file dalam satu direktori (cerita panjang - penyebab utama semakin diperbaiki).
Pertanyaan saya adalah: adakah cara yang lebih cepat untuk melakukan penghitungan? Apa cara paling efisien untuk mendapatkan penghitungan?
ls -l|wc -l
akan dimatikan satu per satu karena total blok pada baris pertamals -l
keluaran-A
bendera.-l
juga bermasalah karena membaca file meta data untuk menghasilkan format daftar yang diperluas. Memaksa TIDAK-l
dengan menggunakan\ls
adalah pilihan yang jauh lebih baik (-1
diasumsikan ketika memipis keluaran). Lihat jawaban Gilles untuk solusi terbaik di sini.ls -l
tidak menghasilkan file tersembunyi maupun entri.
dan..
.ls -a
output termasuk file tersembunyi, termasuk.
dan..
sementarals -A
output termasuk file tersembunyi tidak termasuk.
dan..
. Dalam jawaban Gilles, opsi bashdotglob
shell menyebabkan ekspansi menyertakan file tersembunyi tidak termasuk.
dan..
.Jawaban:
Jawaban singkat:
(Ini termasuk
.
dan..
, jadi kurangi 2.)Ketika Anda mendaftar file dalam direktori, tiga hal umum mungkin terjadi:
ls
perintah melakukan itu.stat
untuk mengambil metadata tentang setiap entri direktori, seperti apakah itu direktori.# 3 adalah yang paling mahal sejauh ini, karena membutuhkan memuat inode untuk setiap file. Sebagai perbandingan, semua nama file yang diperlukan untuk # 1 disimpan secara kompak dalam beberapa blok. # 2 membuang-buang waktu CPU tetapi sering kali bukan pemecah kesepakatan.
Jika tidak ada baris baru dalam nama file, sederhana akan
ls -A | wc -l
memberi tahu Anda berapa banyak file yang ada di direktori. Hati-hati bahwa jika Anda memiliki alias untukls
, ini dapat memicu panggilan kestat
(mis.ls --color
Atauls -F
perlu mengetahui jenis file, yang membutuhkan panggilan kestat
), jadi dari baris perintah, panggilcommand ls -A | wc -l
atau\ls -A | wc -l
untuk menghindari alias.Jika ada baris baru dalam nama file, apakah baris baru terdaftar atau tidak tergantung pada varian Unix. GNU coreutils dan BusyBox default untuk ditampilkan
?
untuk baris baru, jadi mereka aman.Panggil
ls -f
untuk mendaftar entri tanpa menyortirnya (# 2). Ini secara otomatis menyala-a
(setidaknya pada sistem modern). The-f
pilihan adalah di POSIX tapi dengan status yang opsional; sebagian besar implementasi mendukungnya, tetapi tidak BusyBox. Opsi ini-q
menggantikan karakter yang tidak dapat dicetak termasuk baris baru dengan?
; itu POSIX tetapi tidak didukung oleh BusyBox, jadi abaikan saja jika Anda memerlukan dukungan BusyBox dengan mengorbankan overcounting file yang namanya berisi karakter baris baru.Jika direktori tidak memiliki subdirektori, maka sebagian besar versi
find
tidak akan memanggilstat
entri-entrinya (optimisasi direktori daun: direktori yang memiliki jumlah tautan 2 tidak dapat memiliki subdirektori, jadifind
tidak perlu mencari metadata entri kecuali jika kondisi seperti-type
membutuhkannya). Begitufind . | wc -l
juga cara portabel dan cepat untuk menghitung file dalam direktori asalkan direktori tersebut tidak memiliki subdirektori dan bahwa tidak ada nama file yang mengandung baris baru.Jika direktori tidak memiliki subdirektori tetapi nama file mungkin mengandung baris baru, coba salah satu dari ini (yang kedua harus lebih cepat jika didukung, tetapi mungkin tidak begitu terlihat).
Di sisi lain, jangan gunakan
find
jika direktori memiliki subdirektori: bahkanfind . -maxdepth 1
panggilanstat
pada setiap entri (setidaknya dengan GNU find dan BusyBox find). Anda menghindari penyortiran (# 2) tetapi Anda membayar harga pencarian inode (# 3) yang membunuh kinerja.Dalam shell tanpa alat eksternal, Anda dapat menjalankan menghitung file dalam direktori saat ini
set -- *; echo $#
. Ini melewatkan file dot (file yang namanya dimulai dengan.
) dan melaporkan 1 bukannya 0 di direktori kosong. Ini adalah cara tercepat untuk menghitung file dalam direktori kecil karena tidak memerlukan memulai program eksternal, tetapi (kecuali dalam zsh) membuang waktu untuk direktori yang lebih besar karena langkah penyortiran (# 2).Dalam bash, ini adalah cara yang dapat diandalkan untuk menghitung file di direktori saat ini:
Di ksh93, ini adalah cara yang dapat diandalkan untuk menghitung file di direktori saat ini:
Di zsh, ini adalah cara yang dapat diandalkan untuk menghitung file di direktori saat ini:
Jika Anda memiliki
mark_dirs
pilihan set, pastikan untuk mematikannya:a=(*(DNoN^M))
.Di setiap shell POSIX, ini adalah cara yang dapat diandalkan untuk menghitung file di direktori saat ini:
Semua metode ini mengurutkan nama file, kecuali untuk yang zsh.
sumber
find -maxdepth 1
dengan mudah mengimbangi\ls -U
selama Anda tidak menambahkan sesuatu seperti-type
deklarasi yang harus melakukan pemeriksaan lebih lanjut. Apakah Anda yakin GNU menemukan panggilan sebenarnyastat
? Bahkan perlambatanfind -type
tidak ada artinya dibandingkan dengan berapa banyakls -l
rawa jika Anda membuatnya mengembalikan detail file. Di sisi lain pemenang kecepatan yang jelas adalahzsh
menggunakan glob non sorting. (Gumpalan yang disortir lebih lambat 2x daripadals
yang tidak tersortir 2x lebih cepat). Saya ingin tahu apakah tipe sistem file akan secara signifikan mempengaruhi hasil ini.strace
. Ini hanya benar jika direktori tersebut memiliki subdirektori: jika tidakfind
, optimasi direktori leaf-maxdepth 1
akan menghasilkan (bahkan tanpa ), saya seharusnya menyebutkannya. Banyak hal yang dapat mempengaruhi hasilnya, termasuk tipe sistem file (panggilanstat
jauh lebih mahal pada sistem file yang mewakili direktori sebagai daftar linier daripada pada sistem file yang mewakili direktori sebagai pohon), apakah inode semuanya dibuat bersama-sama dan dengan demikian dekat oleh pada disk, cache dingin atau panas, dll.ls -f
telah menjadi cara yang dapat diandalkan untuk mencegah panggilanstat
- ini sering hanya dijelaskan hari ini sebagai "output tidak diurutkan" (yang juga menyebabkan), dan tidak termasuk.
dan..
.-A
dan-U
bukan opsi standar.\ls -afq *[0-9].pdb | wc -l
version sh (AT&T Research) 93u+ 2012-08-01
di sistem berbasis Debian saya,FIGNORE
sepertinya tidak berfungsi. The.
dan..
entri yang dimasukkan ke dalam array yang dihasilkanJauh lebih cepat di komputer saya tetapi
.
direktori lokal ditambahkan ke hitungan.sumber
-type
parameterfind
harus lebih cepat daripadals
-mindepth 1
untuk menghilangkan direktori itu sendiri.ls -1U
sebelum pipa menghabiskan lebih sedikit sumber daya, karena tidak ada upaya untuk mengurutkan entri file, itu hanya membacanya karena mereka diurutkan dalam folder pada disk. Ini juga menghasilkan lebih sedikit output, yang berarti sedikit bekerja untukwc
.Anda juga bisa menggunakan
ls -f
pintasan yang kurang lebih seperti ituls -1aU
.Saya tidak tahu apakah ada cara hemat sumber daya untuk melakukannya melalui perintah tanpa pemipaan.
sumber
Titik perbandingan lain. Meskipun tidak menjadi shell oneliner, program C ini tidak melakukan apa pun yang berlebihan. Perhatikan bahwa file tersembunyi diabaikan agar sesuai dengan output dari
ls|wc -l
(ls -l|wc -l
dimatikan oleh satu karena total blok di baris pertama output).sumber
readdir()
stdio API memang menambah beberapa overhead dan tidak memberi Anda kontrol atas ukuran buffer yang diteruskan ke panggilan sistem yang mendasarinya (getdents
di Linux)Kamu bisa mencoba
perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'
Akan menarik untuk membandingkan timing dengan pipa shell Anda.
sumber
find -maxdepth 1 | wc -l
,\ls -AU | wc -l
danzsh
gumpalan non sorting dan array array berdasarkan). Dengan kata lain itu mengalahkan opsi dengan berbagai inefisiensi seperti menyortir atau membaca properti file asing. Saya berani mengatakan karena itu tidak memberi Anda apa-apa juga, tidak layak menggunakan lebih dari solusi yang lebih sederhana kecuali jika Anda sudah dalam perl :):.
dan..
dalam hitungan, jadi Anda perlu mengurangi dua untuk mendapatkan jumlah file yang sebenarnya (dan subdirektori). Dalam Perl modern,perl -E 'opendir $dh, "."; $i++ while readdir $dh; say $i - 2'
akan melakukannya.Dari jawaban ini , saya bisa memikirkan yang satu ini sebagai solusi yang memungkinkan.
Salin program C di atas ke dalam direktori di mana file harus terdaftar. Kemudian jalankan perintah-perintah ini:
sumber
ls -f
, jangan filterd_type
sama sekali, hanya did->d_ino != 0
; 3) kurangi 2 untuk.
dan..
.ls -f
.Solusi bash-only, tidak memerlukan program eksternal, tetapi tidak tahu seberapa efisien:
sumber
Mungkin cara yang paling efisien sumber daya tidak melibatkan permintaan proses dari luar. Jadi saya bertaruh ...
sumber
Setelah memperbaiki masalah dari jawaban @ Joel, di mana ia ditambahkan
.
sebagai file:find /foo/foo2 -maxdepth 1 | tail -n +2 | wc -l
tail
cukup menghapus baris pertama, artinya.
tidak dihitung lagi.sumber
wc
input tidak terlalu efisien karena overhead meningkat secara linier berkenaan dengan ukuran input. Dalam hal ini, mengapa tidak hanya mengurangi jumlah akhir untuk mengimbanginya dengan satu, yang merupakan operasi waktu yang konstan:echo $(( $(find /foo/foo2 -maxdepth 1 | wc -l) - 1))
let count = $(find /foo/foo2 -maxdepth 1 | wc -l) - 2
os.listdir () dalam python dapat melakukan pekerjaan untuk Anda. Ini memberikan array dari isi direktori, tidak termasuk '.' dan file '..'. Juga, tidak perlu khawatir tentang file dengan karakter khusus seperti '\ n' dalam namanya.
berikut ini adalah waktu yang diambil oleh perintah python di atas dibandingkan dengan perintah 'ls -Af'.
sumber
ls -1 | wc -l
segera muncul di benak saya. Apakahls -1U
lebih cepat daripadals -1
murni akademis - perbedaannya harus diabaikan tetapi untuk direktori yang sangat besar.sumber
Untuk mengecualikan subdirektori dari hitungan, berikut adalah variasi pada jawaban yang diterima dari Gilles:
$(( ))
Ekspansi aritmatika luar mengurangi output dari$( )
subkulit kedua dari yang pertama$( )
. Yang pertama$( )
persis Gilles dari atas. Yang kedua$( )
menampilkan jumlah direktori "yang menghubungkan" ke target. Ini berasal darils -od
(gantikanls -ld
jika diinginkan), di mana kolom yang mencantumkan jumlah tautan keras memiliki itu sebagai makna khusus untuk direktori. The "link" count meliputi.
,..
dan subdirektori apapun.Saya tidak menguji kinerja, tetapi tampaknya akan serupa. Ia menambahkan stat dari direktori target, dan beberapa overhead untuk subkulit dan pipa yang ditambahkan.
sumber
Saya akan berpikir echo * akan lebih efisien daripada perintah 'ls':
sumber
echo 'Hello World'|wc -w
menghasilkan2
.