"Daftar argumen terlalu lama": Bagaimana saya menghadapinya, tanpa mengubah perintah saya?

18

Ketika saya menjalankan perintah seperti ls */*/*/*/*.jpg, saya mendapatkan kesalahan

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

Saya tahu mengapa hal ini terjadi: itu karena ada batas kernel pada jumlah ruang untuk argumen ke perintah. Saran standar adalah mengubah perintah yang saya gunakan, untuk menghindari membutuhkan begitu banyak ruang untuk argumen (misalnya, gunakan finddan xargs).

Bagaimana jika saya tidak ingin mengubah perintah? Bagaimana jika saya ingin tetap menggunakan perintah yang sama? Bagaimana saya bisa membuat hal-hal "hanya berfungsi", tanpa mendapatkan kesalahan ini? Solusi apa yang tersedia?

DW
sumber
Bacaan yang bermanfaat: Bash FAQ 95 . Tanpa mengubah perintah Anda, tidak banyak yang dapat Anda lakukan selain mengkompilasi ulang untuk meningkatkan ukuran maksimum daftar argumen Anda, atau mengubah struktur direktori Anda sehingga ada lebih sedikit file.
jw013
1
@ jw013 berdasarkan versi kernel linux, dimungkinkan untuk menambah daftar argumen - lihat unix.stackexchange.com/a/45161/8979 untuk detail tentang perubahan sistem terbaru.
Ulrich Dangel
@UlrichDangel, Yup, itu mungkin! Lihat jawaban saya; jawaban saya menunjukkan bagaimana melakukannya (di Linux, dengan kernel yang cukup baru).
DW

Jawaban:

26

Di Linux, jumlah ruang maksimum untuk argumen perintah adalah 1/4 dari jumlah ruang tumpukan yang tersedia. Jadi, solusinya adalah meningkatkan jumlah ruang yang tersedia untuk tumpukan.

Versi singkat: jalankan sesuatu seperti

ulimit -s 65536

Versi yang lebih panjang: Jumlah ruang default yang tersedia untuk stack adalah sekitar 8192 KB. Anda dapat melihat jumlah ruang yang tersedia, sebagai berikut:

$ ulimit -s
8192

Pilih jumlah yang lebih besar, dan atur jumlah ruang yang tersedia untuk tumpukan. Misalnya, jika Anda ingin mencoba mengizinkan hingga 65536 KB untuk tumpukan, jalankan ini:

$ ulimit -s 65536

Anda mungkin perlu bermain-main dengan seberapa besar ini perlu, menggunakan coba-coba. Dalam banyak kasus, ini adalah solusi cepat-dan-kotor yang akan menghilangkan kebutuhan untuk memodifikasi perintah dan bekerja keluar sintaks find, xargsdll (meskipun saya menyadari ada manfaat lain untuk melakukannya).

Saya percaya ini khusus untuk Linux. Saya menduga itu mungkin tidak akan membantu pada sistem operasi Unix lain (tidak diuji).

DW
sumber
1
Anda dapat memverifikasi seperti ini bahwa itu berhasil: $ getconf ARG_MAX 2097152 $ ulimit -s 65535 $ getconf ARG_MAX 16776960
Alex
2

Ini artikel Linux Journal memberikan 4 solusi. Hanya solusi keempat yang tidak melibatkan mengubah perintah:

Metode # 4 melibatkan peningkatan secara manual jumlah halaman yang dialokasikan dalam kernel untuk argumen baris perintah. Jika Anda melihat file include / linux / binfmts.h, Anda akan menemukan yang berikut di atas:

/*
 * MAX_ARG_PAGES defines the number of pages allocated for   arguments
 * and envelope for the new program. 32 should suffice, this gives
 * a maximum env+arg of 128kB w/4KB pages!
 */
#define MAX_ARG_PAGES 32

Untuk menambah jumlah memori yang didedikasikan untuk argumen baris perintah, Anda hanya perlu memberikan nilai MAX_ARG_PAGES dengan angka yang lebih tinggi. Setelah pengeditan ini disimpan, cukup kompilasi ulang, instal, dan reboot ke kernel baru seperti yang akan Anda lakukan secara normal.

Pada sistem pengujian saya sendiri, saya berhasil menyelesaikan semua masalah saya dengan menaikkan nilai ini menjadi 64. Setelah pengujian ekstensif, saya tidak mengalami satu masalah sejak beralih. Ini sepenuhnya diharapkan karena bahkan dengan MAX_ARG_PAGESset ke 64, baris perintah terpanjang yang mungkin saya dapat hasilkan hanya akan menempati 256KB memori sistem - tidak terlalu banyak dengan standar perangkat keras sistem saat ini.

Keuntungan dari Metode # 4 jelas. Anda sekarang dapat dengan mudah menjalankan perintah seperti biasa, dan itu berhasil. Kerugiannya sama jelasnya. Jika Anda meningkatkan jumlah memori yang tersedia ke baris perintah di luar jumlah memori sistem yang tersedia, Anda dapat membuat serangan DOS pada sistem Anda sendiri dan menyebabkannya crash. Pada sistem multiuser khususnya, bahkan peningkatan kecil dapat memiliki dampak yang signifikan karena setiap pengguna kemudian dialokasikan memori tambahan. Karena itu selalu uji ekstensif di lingkungan Anda sendiri, karena ini adalah cara paling aman untuk menentukan apakah Metode # 4 adalah opsi yang layak untuk Anda.

Saya setuju bahwa batasannya sangat mengganggu.

Franck Dernoncourt
sumber
1

Alih-alih ls */*/*/*/*.jpg, coba:

echo */*/*/*/*.jpg | xargs ls

xargs(1) tahu, berapa jumlah maksimum argumen pada sistem dan akan memecah input standar untuk memanggil baris perintah yang ditentukan beberapa kali dengan tidak ada argumen lebih dari batas itu, apa pun itu (Anda juga dapat mengaturnya lebih rendah dari maksimum OS menggunakan -nopsi).

Sebagai contoh, anggaplah, batasnya adalah 3 argumen dan Anda memiliki lima file. Dalam hal ini xargsakan dieksekusi lsdua kali:

  1. ls 1.jpg 2.jpg 3.jpg
  2. ls 4.jpg 5.jpg

Seringkali ini sangat cocok, tetapi tidak selalu - misalnya, Anda tidak dapat mengandalkan ls(1) menyortir semua entri untuk Anda dengan benar, karena masing-masing ls-invokasi terpisah akan mengurutkan hanya sebagian dari entri yang diberikan kepadanya xargs.

Meskipun Anda dapat menabrak batas seperti yang disarankan oleh orang lain, masih akan ada batas - dan suatu hari koleksi JPG Anda akan melebihi itu lagi. Anda harus menyiapkan skrip Anda untuk menangani jumlah tak terbatas ...

Mikhail T.
sumber
Terima kasih atas idenya! Ini bukan solusi yang buruk. Dua peringatan: 1. Ini memecah direktori dan nama file yang memiliki spasi dalam namanya, jadi itu bukan pengganti yang sempurna. 2. Apakah itu akan mengalami masalah yang sama dengan Argument list too long, tetapi untuk echobukannya ls, pada shell di mana echotidak ada perintah built-in shell? (Mungkin itu bukan masalah di sebagian besar kerang, jadi mungkin itu tidak relevan.)
DW
1
Ya, karakter khusus dalam nama file adalah masalah. Anda bertaruh terbaik adalah dengan menggunakan finddengan -print0predikat - dan pipa outputnya ke dalam xargsdengan -0pilihan. echoadalah shell built-in dan tidak mengalami batasan baris perintah exec(3).
Mikhail T.