Jawaban:
Satu kalimat sederhana ini harus bekerja di shell apa pun, bukan hanya bash:
ls -1q log* | wc -l
ls -1q akan memberi Anda satu baris per file, meskipun mengandung spasi atau karakter khusus seperti baris baru.
Outputnya disalurkan ke wc -l, yang menghitung jumlah baris.
ls, karena itu menciptakan proses anak. log*diperluas oleh shell, tidak ls, jadi sederhana echoakan dilakukan.
logsdalam direktori yang dimaksud, maka isi dari direktori log tersebut akan dihitung juga. Ini mungkin tidak disengaja.
Anda dapat melakukan ini dengan aman (mis. Tidak akan disadap oleh file dengan spasi atau \nnamanya) dengan bash:
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
Anda perlu mengaktifkan nullglobsehingga Anda tidak mendapatkan literal *.logdalam $logfiles array jika tidak ada file yang cocok. (Lihat Bagaimana "membatalkan" 'set -x'? Untuk contoh cara meresetnya dengan aman.)
shopt -u nullglobharus dilewati jika nullglobtidak disetel maka Anda mulai.
*.logdengan hanya *akan menghitung direktori. Jika file yang ingin Anda hitung memiliki konvensi penamaan tradisional name.extension, gunakan *.*.
Banyak jawaban di sini, tetapi beberapa tidak memperhitungkan
-l)*.logbukanlog*logsyang cocok log*)Inilah solusi yang menangani semuanya:
ls 2>/dev/null -Ubad1 -- log* | wc -l
Penjelasan:
-Umenyebabkan lsuntuk tidak mengurutkan entri, artinya tidak perlu memuat seluruh daftar direktori dalam memori-bmencetak gaya-C yang keluar untuk karakter nongrafik, yang paling penting menyebabkan baris baru dicetak sebagai \n.-amencetak semua file, bahkan file yang tersembunyi (tidak sepenuhnya dibutuhkan ketika glob log*tidak mengandung file tersembunyi)-dmencetak direktori tanpa berusaha membuat daftar isi direktori, yang lsbiasanya akan dilakukan-1 memastikan bahwa itu ada di satu kolom (ls melakukan ini secara otomatis saat menulis ke pipa, sehingga tidak sepenuhnya diperlukan)2>/dev/nullmengarahkan ulang stderr sehingga jika ada 0 file log, abaikan pesan kesalahan. (Catatan yang shopt -s nullglobakan menyebabkan lsdaftar seluruh direktori kerja sebagai gantinya.)wc -lmengkonsumsi daftar direktori seperti yang dihasilkan, jadi output dari lstidak pernah ada dalam memori kapan saja.--Nama file dipisahkan dari perintah menggunakan --agar tidak dipahami sebagai argumen untuk ls(jika log*dihapus)Shell akan diperluas log*ke daftar lengkap file, yang dapat menghabiskan memori jika banyak file, jadi menjalankannya melalui grep lebih baik:
ls -Uba1 | grep ^log | wc -l
Yang terakhir ini menangani direktori file yang sangat besar tanpa menggunakan banyak memori (meskipun menggunakan subkulit). Tidak -dlagi diperlukan, karena itu hanya daftar isi direktori saat ini.
Untuk pencarian rekursif:
find . -type f -name '*.log' -printf x | wc -c
wc -cakan menghitung jumlah karakter dalam output find, sementara-printf x memberitahu finduntuk mencetak satu xuntuk setiap hasil.
Untuk pencarian non-rekursif, lakukan ini:
find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c
-name '*.log'begitu saja maka itu akan menghitung semua file, yang saya butuhkan untuk kasus penggunaan saya. Bendera -maxdepth juga sangat berguna, terima kasih!
find; hanya mencetak sesuatu yang lain dari nama file kata demi kata.
Jawaban yang diterima untuk pertanyaan ini salah, tetapi saya memiliki rep rendah sehingga tidak dapat menambahkan komentar untuk itu.
Jawaban yang benar untuk pertanyaan ini diberikan oleh Mat:
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
Masalah dengan jawaban yang diterima adalah bahwa wc-l menghitung jumlah karakter baris baru, dan menghitungnya bahkan jika mereka mencetak ke terminal sebagai '?' dalam output 'ls-l'. Ini berarti bahwa jawaban yang diterima GAGAL ketika nama file berisi karakter baris baru. Saya telah menguji perintah yang disarankan:
ls -l log* | wc -l
dan secara keliru melaporkan nilai 2 bahkan jika hanya ada 1 file yang cocok dengan pola yang namanya mengandung karakter baris baru. Sebagai contoh:
touch log$'\n'def
ls log* -l | wc -l
Jika Anda memiliki banyak file dan Anda tidak ingin menggunakan shopt -s nullglobsolusi array elegan dan bash, Anda dapat menggunakan find dan sebagainya selama Anda tidak mencetak nama file (yang mungkin berisi baris baru).
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
Ini akan menemukan semua file yang cocok dengan log * dan yang tidak dimulai dengan .* - "not name. *" Redunant, tetapi penting untuk dicatat bahwa default untuk "ls" adalah tidak menampilkan file-file dot, tetapi default untuk menemukan adalah memasukkan mereka.
Ini adalah jawaban yang benar, dan menangani semua jenis nama file yang dapat Anda berikan padanya, karena nama file tidak pernah berpindah antar perintah.
Tapi, shopt nullglobjawabannya adalah jawaban terbaik!
findvs menggunakan lsadalah dua cara berbeda untuk menyelesaikan masalah. findtidak selalu hadir pada mesin, tetapi lsbiasanya,
findmungkin tidak memiliki semua opsi mewah untuk lskeduanya.
-maxdepth 1
findmelakukan ini secara default. Ini dapat membuat kebingungan jika seseorang tidak menyadari ada folder anak tersembunyi, dan mungkin membuatnya menguntungkan untuk digunakan lsdalam beberapa keadaan, yang tidak melaporkan file tersembunyi secara default.
Ini satu liner saya untuk ini.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
set -- tidak melakukan apa pun kecuali menyiapkan kita $#, yang menyimpan sejumlah argumen baris perintah yang diteruskan ke program shell
(tidak cukup reputasi untuk berkomentar)
Ini BUGGY :
ls -1q some_pattern | wc -l
Jika shopt -s nullglobkebetulan diatur, itu mencetak jumlah SEMUA file biasa, bukan hanya yang dengan pola (diuji pada CentOS-8 dan Cygwin). Siapa yang tahu apa yang dilakukan bug tidak berarti lainnyals dimiliki ?
Ini BENAR dan jauh lebih cepat:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
Itu melakukan pekerjaan yang diharapkan.
0.006pada CentOS, dan 0.083pada Cygwin (kalau-kalau digunakan dengan hati-hati).
0.000di CentOS, dan 0.003di Cygwin.
Anda dapat mendefinisikan perintah seperti itu dengan mudah, menggunakan fungsi shell. Metode ini tidak memerlukan program eksternal dan tidak menelurkan proses anak. Itu tidak mencoba lsparsing berbahaya dan menangani karakter "khusus" (spasi putih, baris baru, garis miring terbalik dan sebagainya) baik-baik saja. Itu hanya bergantung pada mekanisme ekspansi nama file yang disediakan oleh shell. Ini kompatibel dengan setidaknya sh, bash, dan zsh.
Baris di bawah ini mendefinisikan fungsi yang disebut countyang mencetak jumlah argumen yang telah dipanggil.
count() { echo $#; }
Sebut saja dengan pola yang diinginkan:
count log*
Agar hasilnya benar ketika pola globbing tidak cocok, opsi shell nullglob(atau failglob- yang merupakan perilaku default pada zsh) harus ditetapkan pada saat ekspansi terjadi. Dapat diatur seperti ini:
shopt -s nullglob # for sh / bash
setopt nullglob # for zsh
Bergantung pada apa yang ingin Anda hitung, Anda mungkin juga tertarik dengan opsi shell dotglob.
Sayangnya, dengan bash setidaknya, tidak mudah untuk mengatur opsi ini secara lokal. Jika Anda tidak ingin mengaturnya secara global, solusi paling mudah adalah menggunakan fungsi ini dengan cara yang lebih berbelit-belit:
( shopt -s nullglob ; shopt -u failglob ; count log* )
Jika Anda ingin memulihkan sintaks yang ringan count log*, atau jika Anda benar-benar ingin menghindari memunculkan subkulit, Anda dapat meretas sesuatu di sepanjang baris:
# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
eval "$_count_saved_shopts"
unset _count_saved_shopts
echo $#
}
alias count='
_count_saved_shopts="$(shopt -p nullglob failglob)"
shopt -s nullglob
shopt -u failglob
count'
Sebagai bonus, fungsi ini lebih umum digunakan. Misalnya:
count a* b* # count files which match either a* or b*
count $(jobs -ps) # count stopped jobs (sh / bash)
Dengan mengubah fungsi menjadi file skrip (atau program C yang setara), dapat dipanggil dari PATH, ia juga dapat dibuat dengan program-program seperti finddan xargs:
find "$FIND_OPTIONS" -exec count {} \+ # count results of a search
Saya telah memberikan jawaban ini banyak pemikiran, terutama mengingat hal -hal yang jangan diurai . Pada awalnya, saya mencoba
<PERINGATAN! TIDAK BEKERJA>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ PERINGATAN! TIDAK BEKERJA>
yang bekerja jika hanya ada nama file seperti
touch $'w\nlf.aa'
tetapi gagal jika saya membuat nama file seperti ini
touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'
Saya akhirnya menemukan apa yang saya tuliskan di bawah. Catatan saya mencoba untuk mendapatkan hitungan semua file dalam direktori (tidak termasuk subdirektori). Saya pikir itu, bersama dengan jawaban oleh @Mat dan @Dan_Yard, serta memiliki setidaknya sebagian besar persyaratan yang ditetapkan oleh @mogsie (saya tidak yakin tentang memori.) Saya pikir jawabannya oleh @mogsie sudah benar, tetapi saya selalu berusaha untuk tidak menguraikan lskecuali itu situasi yang sangat spesifik.
awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'
Lebih mudah dibaca:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -print0) | \
awk '{sum+=$1}END{print sum}'
Ini melakukan pencarian khusus untuk file, membatasi output dengan karakter nol (untuk menghindari masalah dengan spasi dan linefeeds), lalu menghitung jumlah karakter nol. Jumlah file akan menjadi kurang dari jumlah karakter nol, karena akan ada karakter nol di akhir.
Untuk menjawab pertanyaan OP, ada dua hal yang perlu dipertimbangkan
1) Pencarian non-rekursif:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
2) Pencarian rekursif. Perhatikan bahwa apa yang ada di dalam -nameparameter mungkin perlu diubah untuk perilaku yang sedikit berbeda (file tersembunyi, dll.).
awk -F"\0" '{print NF-1}' < \
<(find . -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
Jika ada yang ingin mengomentari bagaimana jawaban ini dibandingkan dengan yang saya sebutkan dalam jawaban ini, silakan lakukan.
Catatan, saya sampai pada proses pemikiran ini sambil mendapatkan jawaban ini .
Inilah yang selalu saya lakukan:
ls log * | awk 'END {print NR}'
awk 'END{print NR}'harus setara dengan wc -l.
ls -1 log* | wc -l
Yang berarti daftar satu file per baris dan kemudian pipa ke perintah jumlah kata dengan pergantian parameter untuk menghitung baris.
-l, karena itu memerlukanstat(2)pada setiap file dan untuk tujuan penghitungan menambahkan apa-apa.