Apakah tidak ada cara untuk melindungi ruang dalam ekspansi backtick (atau $ (...))?
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
dan find
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 bahwa zsh
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:
$ printf '%s\0' -- {1..1302582} | xargs -x0n 2076858 -s 2076858 /bin/true
xargs: argument list too long
$ printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true
(no output)
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:
files=()
while IFS= read -rd '' file; do
files+=("$file")
done <(find ... -print0)
myscriptornonscript "${files[@]}"
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:
$ time { printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true; }
real 0m2.014s
user 0m2.008s
sys 0m0.172s
$ time {
args=()
while IFS= read -rd '' arg; do
args+=( "$arg" )
done < <(printf '%s\0' -- $(echo {1..1302581}))
/bin/true "${args[@]}"
}
bash: /bin/true: Argument list too long
real 107m51.876s
user 107m38.532s
sys 0m7.940s
bash tidak dioptimalkan untuk penanganan array.
man xargs :
-n maks-args
Gunakan argumen maksimal-argumen maksimal per baris perintah. Lebih sedikit daripada argumen max-args akan digunakan jika ukuran (lihat opsi -s) terlampaui, kecuali opsi -x diberikan, dalam hal ini xargs akan keluar.
-s max-chars
Gunakan paling banyak karakter maks-karakter per baris perintah, termasuk perintah dan argumen awal dan penghentian nol di akhir string argumen. Nilai terbesar yang diizinkan bergantung pada sistem, dan dihitung sebagai batas panjang argumen untuk eksekutif, kurang dari ukuran lingkungan Anda, kurang dari 2048 byte ruang kepala. Jika nilai ini lebih dari 128KiB, 128Kib digunakan sebagai nilai default; jika tidak, nilai default adalah maksimum. 1KiB adalah 1024 byte.
-x
Keluar jika ukurannya (lihat opsi -s) terlampaui.
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.