Berikut dari dari: perilaku tak terduga dalam substitusi perintah shell
Saya memiliki perintah yang dapat mengambil daftar besar argumen, beberapa di antaranya dapat berisi ruang secara sah (dan mungkin hal-hal lain)
Saya menulis sebuah skrip yang dapat menghasilkan argumen untuk saya, dengan tanda kutip, tetapi saya harus menyalin dan menempelkan hasilnya misalnya
./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>
Saya mencoba merampingkan ini hanya dengan melakukan
./othercommand $(./somecommand)
dan bertemu dengan perilaku tak terduga yang disebutkan dalam pertanyaan di atas. Pertanyaannya adalah - dapatkah substitusi perintah secara andal digunakan untuk menghasilkan argumen untuk othercommand
diberikan bahwa beberapa argumen memerlukan penawaran dan ini tidak dapat diubah?
shell
arguments
command-substitution
pengguna1207217
sumber
sumber
eval
dapat digunakan, tetapi umumnya tidak disarankan.xargs
adalah sesuatu yang perlu dipertimbangkan jugasomecommand
untuk menjalani parsing shell biasa:
) ... dengan asumsi karakter yang andal tidak akan ada dalam output.Jawaban:
Jika output dikutip dengan benar untuk shell, dan Anda mempercayai output , maka Anda bisa menjalankannya
eval
.Dengan anggapan Anda memiliki shell yang mendukung array, sebaiknya gunakan satu untuk menyimpan argumen yang Anda dapatkan.
Jika
./gen_args.sh
menghasilkan keluaran seperti'foo bar' '*' asdf
, maka kita bisa menjalankaneval "args=( $(./gen_args.sh) )"
untuk mengisi array yang disebutargs
dengan hasilnya. Itu akan menjadi tiga unsurfoo bar
,*
,asdf
.Kita dapat menggunakan
"${args[@]}"
seperti biasa untuk memperluas elemen array secara individual:(Perhatikan tanda kutip.
"${array[@]}"
Perluas ke semua elemen sebagai argumen berbeda yang tidak dimodifikasi. Tanpa tanda kutip, elemen array dapat mengalami pemisahan kata. Lihat misalnya halaman Array di BashGuide .)Namun ,
eval
dengan senang hati akan menjalankan penggantian shell, jadi$HOME
di output akan diperluas ke direktori home Anda, dan substitusi perintah akan benar-benar menjalankan perintah di shell yang sedang berjalaneval
. Output dari"$(date >&2)"
akan membuat elemen array kosong tunggal dan mencetak tanggal saat ini di stdout. Ini menjadi masalah jikagen_args.sh
mendapat data dari beberapa sumber yang tidak dipercaya, seperti host lain melalui jaringan, nama file yang dibuat oleh pengguna lain. Outputnya bisa termasuk perintah sewenang-wenang. (Jikaget_args.sh
itu sendiri berbahaya, tidak perlu mengeluarkan apa pun, itu bisa langsung menjalankan perintah jahat.)Alternatif untuk mengutip shell, yang sulit diurai tanpa eval, adalah menggunakan beberapa karakter lain sebagai pemisah dalam output skrip Anda. Anda harus memilih satu yang tidak diperlukan dalam argumen yang sebenarnya.
Mari kita pilih
#
, dan hasilkan skripnyafoo bar#*#asdf
. Sekarang kita dapat menggunakan ekspansi perintah yang tidak dikutip untuk membagi output dari perintah ke argumen.Anda harus mengatur
IFS
kembali nanti jika Anda bergantung pada pemisahan kata di tempat lain dalam skrip (unset IFS
harus berfungsi untuk menjadikannya default), dan juga gunakanset +f
jika Anda ingin menggunakan globbing nanti.Jika Anda tidak menggunakan Bash atau shell lain yang memiliki array, Anda bisa menggunakan parameter posisi untuk itu. Ganti
args=( $(...) )
denganset -- $(./gen_args.sh)
dan gunakan"$@"
sebagai gantinya"${args[@]}"
. (Di sini, Anda juga perlu mengutip"$@"
, jika tidak, parameter posisi tunduk pada pemisahan kata.)sumber
${args[@]}
- ini tidak bekerja untuk saya sebaliknya"${array[@]}"
juga dengan"$@"
. Keduanya perlu dikutip, atau pemisahan kata memecah elemen array menjadi beberapa bagian.Masalahnya adalah bahwa begitu
somecommand
skrip Anda menampilkan opsi untukothercommand
, opsinya benar-benar hanya teks dan pada belas kasihan standar parsing shell (dipengaruhi oleh apa pun yang$IFS
terjadi dan apa opsi shell berlaku, yang Anda dalam kasus umum tidak akan mengendalikan).Alih-alih menggunakan
somecommand
untuk menampilkan opsi, itu akan lebih mudah, lebih aman, dan lebih kuat untuk menggunakannya untuk meneleponothercommand
. Thesomecommand
Script kemudian akan menjadi skrip wrapper sekitarothercommand
bukannya semacam script pembantu yang Anda harus ingat untuk memanggil dalam beberapa cara khusus sebagai bagian dari baris perintah dariotherscript
. Skrip wrapper adalah cara yang sangat umum untuk menyediakan alat yang hanya memanggil beberapa alat serupa lainnya dengan serangkaian opsi lain (cukup periksa denganfile
perintah apa/usr/bin
yang sebenarnya adalah pembungkus skrip shell).Di
bash
,ksh
atauzsh
, Anda bisa dengan mudah skrip pembungkus yang menggunakan array untuk menampung opsi individualothercommand
seperti:Kemudian panggil
othercommand
(masih dalam skrip pembungkus):Perluasan
"${options[@]}"
akan memastikan bahwa setiap elemenoptions
array dikutip secara individual dan disajikanothercommand
sebagai argumen terpisah.Pengguna wrapper tidak menyadari fakta bahwa itu benar-benar memanggil
othercommand
, sesuatu yang tidak akan benar jika skrip malah hanya menghasilkan opsi baris perintahothercommand
sebagai output.Di
/bin/sh
, gunakan$@
untuk menahan opsi:(
set
Adalah perintah yang digunakan untuk pengaturan parameter posisi$1
,$2
,$3
dll Ini adalah apa yang membuat array$@
dalam standar POSIX shell. Awal--
adalah sinyal untukset
bahwa tidak ada opsi yang diberikan, hanya argumen. The--
adalah benar-benar hanya diperlukan jika Nilai pertama kebetulan sesuatu yang dimulai dengan-
).Perhatikan bahwa ini adalah tanda kutip ganda di sekitar
$@
dan${options[@]}
yang memastikan bahwa elemen-elemen tersebut tidak terpisah kata-kata (dan nama file di-globbed).sumber
set --
?Jika
somecommand
output dalam sintaks shell yang andal bagus, Anda dapat menggunakaneval
:Tetapi Anda harus yakin bahwa output memiliki kutipan yang valid dan semacamnya, jika tidak, Anda mungkin juga menjalankan perintah di luar skrip:
Catatan yang
echo rm bar baz test.sh
tidak diteruskan ke skrip (karena;
) dan dijalankan sebagai perintah terpisah. Saya menambahkan|
sekitar$var
untuk menyoroti ini.Secara umum, kecuali Anda benar-benar dapat mempercayai output
somecommand
, tidak mungkin untuk menggunakan outputnya untuk membangun string perintah secara andal.sumber