Apakah ada alternatif yang lebih ringkas untuk pemipaan ke wc untuk menghitung file dalam direktori

12

Jika saya melakukannya ls -1 target_dir | wc -l, saya mendapatkan jumlah file dalam direktori. Saya menemukan ini agak rumit. Apakah ada cara yang lebih elegan atau ringkas?

codecowboy
sumber
2
Anda tidak memerlukan "-1" saat memipakan ke wc.
Steve
lssudah memberikan jumlah total, lalu bagaimana ls -l | head -1? Jadikan sebagai alias jika Anda menginginkan sesuatu yang lebih pendek.
Daniel Wagner
2
@DanielWagner Output "total: nnn" dengan ls -lmenunjukkan ukuran total file, bukan jumlah file.
David Richerby
2
Perlu diingat bahwa ls | wc -lakan memberi Anda hitungan yang salah jika ada nama file yang berisi baris baru.
chepner
Ini tergantung pada sistem file, dan menghitung direktori + 2 dalam direktori. Jawabannya memiliki 2 tambahan (karena ia menghitung sendiri, dan induknya). stat -c %h .memberikan informasi yang sama denganls -ld . | cut -d" " -f 2
ctrl-alt-delor

Jawaban:

12

Dengan asumsi bash 4+ (yang dimiliki oleh semua versi Ubuntu yang didukung):

num_files() (
    shopt -s nullglob
    cd -P -- "${1-.}" || return
    set -- *
    echo "$#"
)

Sebut saja sebagai num_files [dir]. dirbersifat opsional, jika tidak menggunakan direktori saat ini. Versi asli Anda tidak menghitung file tersembunyi, demikian juga ini tidak. Jika Anda menginginkan itu, shopt -s dotglobsebelumnya set -- *.

Contoh asli Anda tidak hanya menghitung file biasa, tetapi juga direktori dan perangkat lain - jika Anda benar-benar hanya ingin file biasa (termasuk symlink ke file biasa), Anda perlu memeriksanya:

num_files() (
    local count=0

    shopt -s nullglob
    cd -P -- "${1-.}" || return
    for file in *; do
        [[ -f $file ]] && let count++
    done
    echo "$count"
)

Jika Anda menemukan GNU, sesuatu seperti ini juga merupakan opsi (perhatikan bahwa ini termasuk file tersembunyi, yang tidak dilakukan perintah asli Anda):

num_files() {
    find "${1-.}" -maxdepth 1 -type f -printf x | wc -c
}

(ubah -typeke -xtypejika Anda juga ingin menghitung symlink ke file biasa).

Chris Down
sumber
Tidak akan setgagal jika ada banyak file? Saya pikir Anda mungkin harus menggunakan xargsdan menjumlahkan kode untuk membuatnya bekerja dalam kasus umum.
l0b0
1
Juga shopt -s dotglobjika Anda ingin file mulai .dihitung
Digital Trauma
1
@ l0b0 Saya tidak berpikir setakan gagal dalam keadaan ini, karena kita sebenarnya tidak melakukan exec. Intinya, pada sistem saya, getconf ARG_MAXmenghasilkan 262144, tetapi jika saya lakukan test_arg_max() { set -- {1..262145}; echo $#; }; test_arg_max, itu dengan senang hati menjawab 262145.
kojiro
@ DavidRicherby -maxdepthbukan POSIX.
Chris Down
4
@MichaelMartinez Menulis kode yang jelas bukan pengganti untuk menulis kode yang benar.
Chris Down
3

f=(target_dir/*);echo ${#f[*]}

berfungsi dengan benar untuk file dengan spasi, baris baru, dll. dalam nama.

Aaron Davies
sumber
dapatkah Anda memberikan beberapa konteks? Haruskah ini menggunakan skrip bash?
codecowboy
itu bisa. Anda juga bisa memasukkannya langsung ke shell. versi itu menganggap Anda menginginkan direktori saat ini; saya telah mengeditnya sehingga lebih dekat dengan pertanyaan Anda. pada dasarnya ia menciptakan variabel array shell yang berisi semua file dalam direktori, kemudian mencetak hitungan array itu. harus bekerja di shell apa pun dengan array - bash, ksh, zsh, dll. - tetapi mungkin bukan sh / ash / dash.
Aaron Davies
2

lsadalah multi-kolom hanya jika itu output langsung ke terminal, Anda dapat menghapus opsi "-1", Anda dapat menghapus opsi wc"-l", hanya membaca nilai pertama (solusi malas, tidak digunakan untuk bukti hukum, investigasi kriminal, misi kritis, ops taktis ..).

ls target | wc 
Emmanuel
sumber
5
Ini gagal untuk nama file yang mengandung baris baru.
l0b0
@ Emmanuel Anda harus menguraikan hasil Anda wcuntuk mendapatkan jumlah file bahkan dalam kasus sepele, jadi bagaimana ini bahkan sebuah solusi?
l0b0
@ Emmanuel Ini bisa gagal jika targetadalah gumpalan yang, ketika diperluas, mencakup beberapa hal yang dimulai dengan tanda hubung. Misalnya, buat direktori baru, masuk ke dalamnya dan lakukan touch -- {1,2,3,-a}.txt && ls *|wc(NB: gunakan rm -- *.txtuntuk menghapus file-file itu.)
David Richerby
Apakah maksud Anda wc -l? Kalau tidak, Anda mendapatkan jumlah baris, kata, dan byte dari lsoutput. Itulah yang dikatakan David Richerby: Anda harus menguraikannya lagi.
erik
@ erik saya wctidak punya argumen Anda tidak perlu menguraikan jika otak Anda tahu bahwa argumen pertama adalah baris baru.
Emmanuel
2

Jika itu adalah kejelasan yang Anda cari (daripada ketepatan yang tepat ketika berurusan dengan file dengan baris baru dalam nama mereka, dll.), Saya sarankan hanya aliasing wc -lke lc("jumlah baris"):

$ alias lc='wc -l'
$ ls target_dir|lc

Seperti yang telah dicatat orang lain, Anda tidak perlu -1opsi ls, karena ini otomatis ketika lsmenulis ke sebuah pipa. (Kecuali jika Anda lsselalu menggunakan mode kolom. Saya pernah melihatnya sebelumnya, tetapi tidak terlalu sering.)

Sebuah lcalias cukup berguna secara umum, dan untuk pertanyaan ini, jika Anda melihat kasus "hitung direktori saat ini", ls|lckira-kira sepintas yang Anda dapat.

Aaron Davies
sumber
2

Sejauh ini pendekatan Harun adalah satu-satunya pendekatan yang lebih ringkas daripada pendekatan Anda. Versi yang lebih tepat dari pendekatan Anda mungkin terlihat seperti:

ls -aR1q | grep -Ecv '^\./|/$|^$'

Itu secara rekursif mendaftar semua file - bukan direktori - satu per baris termasuk file .dot di bawah direktori saat ini menggunakan shell shell yang diperlukan untuk mengganti karakter yang tidak dapat dicetak. grep menyaring semua daftar direktori induk atau .. atau * / atau baris kosong - jadi seharusnya hanya ada satu baris per file - jumlah total grep yang dikembalikan kepada Anda. Jika Anda ingin direktori anak dimasukkan juga:

ls -aR1q | grep -Ecv '^\.{1,2}/|^$'

Hapus -Rdalam kedua kasus jika Anda tidak ingin hasil rekursif.

mikeserv
sumber
1
Saya cenderung lebih suka melakukan hal semacam itu dengan find. Jika Anda hanya menginginkan hitungan, ini akan berhasil: find -mindepth 1 -maxdepth 1 -printf '\n'|wc -l(lepaskan kontrol kedalaman untuk mendapatkan hasil rekursif).
Aaron Davies
@ AaronDavies - itu tidak benar-benar berfungsi. Masukkan baris baru di salah satu nama file itu dan lihat sendiri. Juga, untuk melakukan hal yang sama dengan mudah Anda lakukan: find . \! -name . -prune | wc -l- yang masih tidak berfungsi, tentu saja.
mikeserv
1
Saya tidak mengikuti - printfinstruksi mencetak string konstan (baris baru) yang tidak menyertakan nama file sama sekali, sehingga hasilnya tidak tergantung pada nama file yang aneh. Trik ini tidak dapat dilakukan sama sekali dengan findyang tidak mendukung printf, tentu saja.
Aaron Davies
@ AaronDavies - oh, benar. Saya berasumsi nama file dimasukkan. Ini bisa dilakukan dengan mudah, tentu saja:find .//. \!. -name . -prune | grep -c '^\.//\.'
mikeserv
cemerlang! /menjadi satu-satunya karakter lain yang tidak dapat muncul dalam nama file, .//.urutannya dijamin akan muncul tepat sekali untuk setiap file, kan? beberapa pertanyaan - mengapa .//., dan mengapa -prune? kapan ini berbeda find . \! -name . | grep -c '^\.'? (Saya berasumsi bahwa kesalahan .Anda \!.adalah kesalahan ketik.)
Aaron Davies