Saya mencoba menjalankan skrip dengan daftar nama file yang dikumpulkan oleh find
. Tidak ada yang istimewa, hanya kira-kira seperti ini:
$ myscript `find . -name something.txt`
Masalahnya adalah bahwa beberapa nama path mengandung spasi, sehingga mereka dapat dipecah menjadi dua nama yang tidak valid pada perluasan argumen. Biasanya saya akan mengelilingi nama-nama dengan tanda kutip, tetapi di sini mereka disisipkan oleh ekspansi backquote. Saya sudah mencoba memfilter keluaran find
dan mengelilingi setiap nama file dengan tanda kutip, tetapi pada saat bash melihatnya, sudah terlambat untuk menghapusnya dan mereka diperlakukan sebagai bagian dari nama file:
$ myscript `find . -name something.txt | sed 's/.*/"&"/'`
No such file or directory: '"./somedir/something.txt"'
Ya, itulah aturan untuk bagaimana baris perintah diproses, tetapi bagaimana cara mengatasinya?
Ini memalukan, tetapi saya gagal menghasilkan pendekatan yang tepat. Saya akhirnya menemukan cara untuk melakukannya dengan xargs -0 -n 10000
... tapi itu adalah hack yang jelek sehingga saya masih ingin bertanya: Bagaimana saya mengutip hasil ekspansi backquote, atau mencapai efek yang sama dengan cara lain?
Edit: Saya bingung tentang fakta bahwa xargs
tidak mengumpulkan semua argumen ke dalam daftar argumen tunggal, kecuali jika mengatakan sebaliknya atau batas sistem mungkin dilampaui. Terima kasih kepada semua orang karena telah meluruskan saya! Yang lain, ingatlah ini ketika Anda membaca jawaban yang diterima karena tidak ditunjukkan secara langsung.
Saya telah menerima jawabannya, tetapi pertanyaan saya tetap: Apakah tidak ada cara untuk melindungi ruang dalam backtick (atau $(...)
) ekspansi? (Perhatikan bahwa solusi yang diterima adalah jawaban non-bash).
IFS="
, baris baru,"
). Tetapi apakah ada kebutuhan untuk mengeksekusi skrip di atas semua nama file? Jika tidak, coba gunakan find sendiri untuk menjalankan skrip untuk setiap file.Jawaban:
Anda dapat melakukan hal berikut menggunakan beberapa implementasi dari
find
danxargs
seperti ini.atau, secara standar, hanya
find
:Contoh
Katakanlah saya memiliki direktori contoh berikut.
Sekarang katakanlah saya punya ini untuk
./myscript
.Sekarang ketika saya menjalankan perintah berikut.
Atau ketika saya menggunakan formulir ke-2 seperti:
Detail
temukan + xargs
Kedua metode di atas, meskipun terlihat berbeda, pada dasarnya sama. Yang pertama adalah mengambil output dari find, membaginya menggunakan NULLs (
\0
) melalui-print0
switch to find. Inixargs -0
dirancang khusus untuk mengambil input yang dipisah menggunakan NULLs. Sintaksis non-standar diperkenalkan oleh GNUfind
danxargs
tetapi juga ditemukan saat ini di beberapa yang lain seperti kebanyakan BSD terbaru. The-r
opsi diperlukan untuk menghindari memanggilmyscript
jikafind
temuan apa-apa dengan GNUfind
tapi tidak dengan BSD.CATATAN: Seluruh pendekatan ini bergantung pada fakta bahwa Anda tidak akan pernah melewatkan string yang sangat panjang. Jika ya, maka doa kedua dari
./myscript
akan dimulai dengan sisa hasil selanjutnya dari penemuan.temukan dengan +
Itulah cara standar (meskipun baru ditambahkan relatif baru (2005) ke dalam implementasi GNU
find
). Kemampuan untuk melakukan apa yang kita lakukanxargs
benar-benar dibangun ke dalamfind
. Jadifind
akan menemukan daftar file dan kemudian meneruskan daftar itu sebanyak mungkin argumen sesuai dengan perintah yang ditentukan setelah-exec
(catatan yang{}
hanya bisa terakhir sebelum+
dalam kasus ini), menjalankan perintah beberapa kali jika diperlukan.Kenapa tidak mengutip?
Pada contoh pertama kita mengambil jalan pintas dengan sepenuhnya menghindari masalah dengan mengutip, dengan menggunakan NULL untuk memisahkan argumen. Kapan
xargs
diberikan daftar ini diperintahkan untuk membagi pada NULLs secara efektif melindungi atom perintah individu kita.Pada contoh kedua, kami menjaga agar hasilnya tetap internal
find
dan jadi ia tahu apa masing-masing atom file, dan akan menjamin untuk menanganinya dengan tepat, sehingga menghindari bisnis whoie mengutipnya.Ukuran maksimum dari baris perintah?
Pertanyaan ini muncul dari waktu ke waktu sehingga sebagai bonus saya menambahkannya ke jawaban ini, terutama agar saya dapat menemukannya di masa depan. Anda dapat menggunakan
xargs
untuk melihat seperti apa batas lingkungan itu:sumber
+
argumen untukfind
(dan Anda gunakan+
dalam prosa juga, jadi saya melewatkan penjelasan Anda pertama kali). Tapi lebih tepatnya, saya salah mengerti apa yangxargs
dilakukan secara default !!! Dalam tiga dekade penggunaan Unix, saya belum pernah menggunakannya sampai sekarang, tapi saya pikir saya tahu kotak alat saya ...xargs
adalah iblis dari sebuah perintah. Anda harus membacanya danfind
halaman manual berkali-kali untuk mendapatkan apa yang bisa mereka lakukan. Mungkin sakelar saling berlawanan satu sama lain sehingga menambah kebingungan.$(..)
sekarang sebagai gantinya. Ini secara otomatis menangani penumpukan kutipan dll. Backticks sudah ditinggalkan.Di atas,
find
temukan semua nama file yang cocok dan berikan sebagai argumenmyscript
. Ini berfungsi dengan nama file terlepas dari spasi atau karakter ganjil lainnya.Jika semua nama file sesuai pada satu baris, maka skrip dieksekusi sekali. Jika daftar terlalu panjang untuk ditangani oleh shell, maka find akan menjalankan myscript beberapa kali sesuai kebutuhan.
LEBIH BANYAK: Berapa banyak file yang pas di baris perintah?
man find
mengatakan bahwafind
build it memerintahkan baris "seperti halnya xargs membangunnya". Dan,man xargs
bahwa batasannya bergantung pada sistem dan Anda dapat menentukannya dengan menjalankannyaxargs --show-limits
. (getconf ARG_MAX
juga kemungkinan). Di Linux, batas biasanya (tetapi tidak selalu) sekitar 2 juta karakter per baris perintah.sumber
Beberapa tambahan untuk jawaban baik @ slm.
Batasan pada ukuran argumen adalah pada
execve(2)
system call (sebenarnya, itu pada ukuran kumulatif dari argumen dan string lingkungan dan pointer). Jikamyscript
ditulis dalam bahasa yang dapat ditafsirkan oleh shell Anda, maka mungkin Anda tidak perlu menjalankannya , Anda bisa meminta shell Anda menafsirkannya tanpa harus mengeksekusi juru bahasa lain.Jika Anda menjalankan skrip sebagai:
Itu seperti:
Kecuali bahwa itu ditafsirkan oleh anak dari shell saat ini, bukannya mengeksekusi (yang akhirnya melibatkan eksekusi
sh
(atau apa pun yang dia-bang baris tentukan jika ada) dengan argumen lebih banyak lagi).Sekarang jelas, Anda tidak dapat menggunakan
find -exec {} +
dengan.
perintah, karena.
menjadi perintah builtin dari shell, itu harus dieksekusi oleh shell, bukan olehfind
.Dengan
zsh
, mudah:Atau:
Meskipun dengan
zsh
, Anda tidak perlufind
di tempat pertama karena sebagian besar fiturnya dibangun kezsh
globbing.bash
Namun variabel tidak dapat berisi karakter NUL, jadi Anda harus menemukan cara lain. Salah satu caranya adalah:Anda juga dapat menggunakan globing rekursif gaya zsh dengan
globstar
opsi padabash
4.0 dan yang lebih baru:Perhatikan bahwa
**
mengikuti symlink ke direktori sampai diperbaiki dibash
4.3. Perhatikan juga bahwabash
tidak menerapkanzsh
kualifikasi globbing sehingga Anda tidak akan mendapatkan semua fitur difind
sana.Alternatif lain adalah menggunakan GNU
ls
:Metode di atas juga dapat digunakan jika Anda ingin memastikan
myscript
yang dieksekusi hanya sekali (gagal jika daftar argumen terlalu besar). Pada versi Linux terbaru, Anda dapat menaikkan dan bahkan mengangkat batasan itu pada daftar argumen dengan:(Ukuran tumpukan 1GiB, seperempatnya dapat digunakan untuk daftar arg + env).
(tidak ada batas)
sumber
Di sebagian besar sistem, ada batas panjang baris perintah yang diteruskan ke program apa pun, menggunakan
xargs
atau-exec command {} +
. Dariman find
:Doa akan jauh lebih sedikit, tetapi tidak dijamin menjadi satu. Yang harus Anda lakukan adalah membaca nama file yang dipisahkan NUL dalam skrip dari stdin, mungkin berdasarkan pada argumen commandline
-o -
. Saya akan melakukan sesuatu seperti:dan mengimplementasikan argumen opsi yang
myscript
sesuai.sumber
xargs
kerjanya. Solusi Anda memang yang paling kuat, tetapi ini berlebihan dalam hal ini.Tidak, tidak ada. Mengapa demikian?
Bash tidak memiliki cara untuk mengetahui apa yang harus dilindungi dan apa yang tidak.
Tidak ada array di file / pipa unix. Itu hanya aliran byte. Perintah di dalam
``
atau$()
menampilkan aliran, yang bash menelan dan memperlakukan sebagai string tunggal. Karena itu, Anda hanya memiliki dua pilihan: memasukkannya ke dalam tanda kutip, untuk membuatnya sebagai satu string, atau meletakkannya dalam keadaan telanjang, sehingga bash membaginya sesuai dengan perilaku yang dikonfigurasi.Jadi apa yang harus Anda lakukan jika Anda menginginkan array adalah menentukan format byte yang memiliki array, dan itulah yang disukai
xargs
danfind
dilakukan oleh alat : Jika Anda menjalankannya dengan-0
argumen, mereka bekerja sesuai dengan format array biner yang mengakhiri elemen dengan byte nol, menambahkan semantik ke aliran byte sebaliknya buram.Sayangnya,
bash
tidak dapat dikonfigurasikan untuk membagi string pada byte nol. Terima kasih kepada /unix//a/110108/17980 untuk menunjukkan kepada kami bahwazsh
dapat.xargs
Anda ingin perintah Anda dijalankan sekali, dan Anda mengatakan itu
xargs -0 -n 10000
menyelesaikan masalah Anda. Tidak, itu memastikan bahwa jika Anda memiliki lebih dari 10.000 parameter, perintah Anda akan berjalan lebih dari sekali.Jika Anda ingin menjadikannya benar-benar berjalan sekali atau gagal, Anda harus memberikan
-x
argumen dan-n
argumen yang lebih besar dari-s
argumen (benar-benar: cukup besar sehingga sejumlah besar argumen panjang nol ditambah nama perintah tidak cocok dengan yang-s
ukuran). ( man xargs , lihat kutipan jauh di bawah)Sistem saya saat ini memiliki tumpukan terbatas sekitar 8M, jadi inilah batas saya:
pesta
Jika Anda tidak ingin melibatkan perintah eksternal, loop sambil-membaca mengumpankan array, seperti yang ditunjukkan di /unix//a/110108/17980 , adalah satu-satunya cara bagi bash untuk membagi berbagai hal di byte nol.
Gagasan untuk sumber skrip
( . ... "$@" )
untuk menghindari batas ukuran tumpukan keren (saya mencobanya, itu berhasil!), Tetapi mungkin tidak penting untuk situasi normal.Menggunakan fd khusus untuk pipa proses itu penting jika Anda ingin membaca sesuatu yang lain dari stdin, tetapi jika tidak, Anda tidak akan membutuhkannya.
Jadi, cara "asli" paling sederhana, untuk kebutuhan rumah tangga sehari-hari:
Jika Anda suka pohon proses Anda bersih dan enak dilihat, metode ini memungkinkan Anda melakukannya
exec mynonscript "${files[@]}"
, yang menghapus proses bash dari memori, menggantinya dengan perintah yang disebut.xargs
akan selalu tetap ada dalam memori saat perintah yang dipanggil berjalan, bahkan jika perintah tersebut hanya akan berjalan sekali.Yang menentang metode bash asli adalah ini:
bash tidak dioptimalkan untuk penanganan array.
man xargs :
sumber
ls "what is this"
vsls `echo '"what is this"'`
. Seseorang lalai menerapkan pemrosesan penawaran untuk hasil backquotes.$(...)
) ekspansi?", Jadi sepertinya tepat untuk mengabaikan pemrosesan yang tidak dilakukan dalam situasi itu.bash
tidak mendukungnya seperti yang tampaknyazsh
.printf "%s\0"
danxargs -0
untuk rute di sekitar situasi mengutip di mana alat perantara akan melewati parameter melalui string yang diuraikan oleh shell. Mengutip selalu kembali menggigit Anda.