Daftar argumen terlalu panjang untuk ls

48

Saya mendapatkan kesalahan berikut ketika mencoba ke ls *.txt | wc -ldirektori yang berisi banyak file:

-bash: /bin/ls: Argument list too long

Apakah ambang batas "Daftar argumen" ini bergantung pada distro atau spesifikasi komputer? Biasanya, saya akan mengirim hasil besar seperti itu ke beberapa perintah lain ( wc -lmisalnya), jadi saya tidak peduli dengan batas terminal.

zahypeti
sumber
6
Itu dianggap sebagai keluaran parsingls , yang merupakan ide yang buruk, jadi lebih baik menghindarinya. Untuk menghitung lihat Apa cara terbaik untuk menghitung jumlah file dalam direktori? , untuk solusi yang rumit, lihat mengapa loop tidak menimbulkan kesalahan "argumen terlalu lama"? .
manatwork
@manatwork Ya, saya juga melihat pertanyaan itu. Hanya ingin tahu cara yang lebih baik untuk menggunakan atau mengarahkan keluaran panjang dari sebuah perintah dengan cara yang lebih umum.
Anda dapat menggunakan getconf ARG_MAX untuk mendapatkan batas pada sebagian besar sistem berbasis unix
Prasanth

Jawaban:

49

Pesan kesalahan Anda daftar argumen terlalu lama berasal dari * dari ls *.txt.

Batasan ini adalah keamanan untuk program biner dan Kernel Anda. Anda akan melihat pada halaman ini informasi lebih lanjut tentang itu, dan bagaimana itu digunakan dan dihitung.

Tidak ada batasan ukuran pipa. Jadi, Anda cukup mengeluarkan perintah ini:

find -type f -name '*.txt'  | wc -l

NB: Di Linux modern, karakter aneh dalam nama file (seperti baris baru) akan lolos dengan alat seperti lsatau find, tetapi masih ditampilkan dari * . Jika Anda menggunakan Unix lama, Anda akan memerlukan perintah ini

find -type f -name '*.txt' -exec echo \;  | wc -l

NB2: Saya bertanya-tanya bagaimana seseorang dapat membuat file dengan baris baru dalam namanya. Tidak sulit, setelah Anda tahu caranya:

touch "hello
world"
Coren
sumber
1
Saya memodifikasinya sedikit agar berfungsi jika ada nama file dengan baris baru di dalamnya. Anda mungkin juga ingin menambahkan -maxdepth 1jika Anda tidak bermaksud menghitung file dalam subdirektori.
Shawn J. Goff
Anda tidak membutuhkannya -exec echo \;.
Mikel
@ ShawnJ.Goff Saya sudah mengujinya. Tidak perlu `echo` dalam versi GNU find saat ini
Coren
@Coren @Mikel - tidak semua orang memiliki GNU find. Pada findOS X dan pada sistem berbasis busybox, dan saya kira setiap sistem berbasis BSD menunjukkan nama file dengan baris baru di dalamnya, yang akan mengacaukan hitungan.
Shawn J. Goff
Hah? wc -lsedang menghitung baris baru. Jadi kami ingin memiliki baris baru.
Mikel
11

Ini terutama tergantung pada versi kernel Linux Anda.

Anda harus dapat melihat batas untuk sistem Anda dengan menjalankan

getconf ARG_MAX

yang memberitahu Anda jumlah byte maksimum yang bisa dimiliki oleh baris perintah setelah diperluas oleh shell.

Di Linux <2.6.23, batasnya biasanya 128 KB.

Di Linux> = 2.6.25, batasnya adalah 128 KB, atau 1/4 dari ukuran tumpukan Anda (lihat ulimit -s), mana yang lebih besar.

Lihat halaman manual execve (2) untuk semua detailnya.


Sayangnya, perpipaan ls *.txttidak akan memperbaiki masalah, karena batasnya ada di sistem operasi, bukan shell.

Shell memperluas *.txt, lalu mencoba menelepon

exec("ls", "a.txt", "b.txt", ...)

dan Anda memiliki banyak file yang cocok *.txtsehingga Anda melebihi batas 128 KB.

Anda harus melakukan sesuatu seperti

find . -maxdepth 1 -name "*.txt" | wc -l

sebagai gantinya.

(Dan lihat komentar Shawn J. Goff di bawah ini tentang nama file yang berisi baris baru.)

Mikel
sumber
Maaf karena tidak dapat mengunggah jawaban. Perlu lebih banyak reputasi. :( Terima kasih semuanya !!
Bisakah Anda menjelaskan apa .dan apa -maxdepth 1artinya di baris terakhir? Terima kasih! : D
Guilherme Salomé
2
@ GuilhermeSalomé .berarti direktori saat ini, -maxdepth 1berarti direktori tersebut tidak terlihat di subdirektori. Ini dimaksudkan agar sesuai dengan file yang sama dengan *.txt.
Mikel
9

Solusi lain:

ls | grep -c '\.txt$'

Meskipun lsmenghasilkan lebih banyak output daripada ls *.txtmenghasilkan (atau upaya untuk menghasilkan), itu tidak mengalami masalah "argumen terlalu lama", karena Anda tidak meneruskan argumen apa punls . Perhatikan bahwa grepmengambil ekspresi reguler daripada pola pencocokan file.

Anda mungkin ingin menggunakan:

ls -U | grep -c '\.txt$'

(dengan asumsi versi Anda lsmendukung opsi ini). Ini memberitahu Anda lsuntuk tidak mengurutkan outputnya, yang dapat menghemat waktu dan memori - dan dalam hal ini urutannya tidak masalah, karena Anda hanya menghitung file. Sumber daya yang dihabiskan untuk menyortir output biasanya tidak signifikan, tetapi dalam hal ini kami sudah tahu Anda memiliki jumlah *.txtfile yang sangat besar .

Dan Anda harus mempertimbangkan mengatur ulang file Anda sehingga Anda tidak memiliki begitu banyak dalam satu direktori. Ini mungkin atau mungkin tidak layak.

Keith Thompson
sumber
1

MAX_ARG_PAGES tampaknya merupakan parameter kernel. Menggunakan finddan xargsmerupakan kombinasi khas untuk mengatasi batas ini, tetapi saya tidak yakin itu akan berhasil wc.

Memipipkan output find . -name \*\.txtke file dan menghitung baris dalam file itu harus berfungsi sebagai solusi.

Bram
sumber
Anda dapat melakukan apa saja dengan lsoutput, tidak akan menyelesaikan ini. Selama * .txt wildcard diperluas melebihi batas, akan gagal bahkan sebelum memulai lsdan menghasilkan output apa pun.
manatwork
Benar, saya telah memperbarui jawaban saya.
Bram
Lebih baik. Tetapi untuk menjadikannya pengganti, lsAnda harus menentukan -maxdepth 1untuk menghindari pemindaian subdirektori secara rekursif.
manatwork
Maaf karena tidak dapat mengunggah jawaban. Perlu lebih banyak reputasi. :(
0

Ini mungkin kotor tetapi berfungsi untuk kebutuhan saya dan dalam kompetensi saya. Saya tidak berpikir itu berkinerja sangat cepat tetapi memungkinkan saya untuk melanjutkan hari saya.

ls | grep jpg | <something>

Saya mendapatkan daftar panjang 90.000 jpg dan mengirimkannya ke avconv untuk menghasilkan timelapse.

Saya sebelumnya menggunakan ls * .jpg | avconv sebelum saya mengalami masalah ini.

Richard Bettridge
sumber