Saya ingin secara dinamis memberikan nilai ke variabel menggunakan eval
. Contoh dummy berikut berfungsi:
var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange
Namun, ketika nilai variabel berisi spasi, eval
mengembalikan kesalahan, meskipun $var_value
ditempatkan di antara tanda kutip ganda:
var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found
Adakah cara untuk menghindari ini?
eval
cara itu salah. Anda memperluas$var_value
sebelum meneruskannya keeval
yang artinya akan ditafsirkan sebagai kode shell! (coba misalnya denganvar_value="';:(){ :|:&};:'"
)eval
(yang merupakan salah satu alasan saya katakan Anda tidak boleh menggunakaneval
).$var_value
adalah salah satu inversi kuotasi - dengan asumsi nilai aman untuk$var_name
(yang bisa sama berbahayanya dengan asumsi, sebenarnya) , maka Anda harus menyertakan tanda kutip sisi kanan dalam tanda kutip tunggal - tidak dan sebaliknya.eval
, menggunakanprintf
dan format -bash
spesifik%q
. Ini masih bukan rekomendasi untuk digunakaneval
, tapi saya pikir ini lebih aman daripada sebelumnya. Fakta bahwa Anda perlu melakukan banyak upaya untuk membuatnya berfungsi adalah bukti bahwa Anda harus menggunakandeclare
atau menamai referensi.set -- a bunch of args; eval "process2 $(process1 "$@")"
manaprocess1
hanya mencetak angka yang dikutip seperti"${1}" "${8}" "${138}"
. Itu gila sederhana - dan semudah'"${'$((i=$i+1))'}" '
dalam banyak kasus. referensi yang diindeks membuatnya aman, kuat, dan cepat . Masih - saya terbalik.Cara yang baik untuk bekerja
eval
adalah menggantinya denganecho
untuk pengujian.echo
daneval
bekerja sama (jika kita menyisihkan\x
ekspansi yang dilakukan oleh beberapaecho
implementasi sepertibash
dalam beberapa kondisi).Kedua perintah bergabung dengan argumen mereka dengan satu spasi di antaranya. Perbedaannya adalah bahwa
echo
menampilkan hasil saateval
mengevaluasi / menginterpretasikan sebagai kode shell hasil.Jadi, untuk melihat kode shell apa
akan mengevaluasi, Anda dapat menjalankan:
Bukan itu yang Anda inginkan, yang Anda inginkan adalah:
Juga, menggunakan di
$(echo ...)
sini tidak masuk akal.Untuk menampilkan yang di atas, Anda akan menjalankan:
Jadi, untuk menafsirkannya, itu hanya:
Perhatikan bahwa ini juga dapat digunakan untuk mengatur elemen array individual:
Seperti yang orang lain katakan, jika Anda tidak peduli kode Anda
bash
spesifik, Anda dapat menggunakandeclare
sebagai:Namun perhatikan bahwa ia memiliki beberapa efek samping.
Ini membatasi ruang lingkup variabel ke fungsi di mana itu dijalankan. Jadi Anda tidak dapat menggunakannya misalnya dalam hal-hal seperti:
Karena itu akan mendeklarasikan
foo
variabel lokalsetvar
sehingga tidak akan berguna.bash-4.2
menambahkan-g
opsi untukdeclare
mendeklarasikan variabel global , tapi bukan itu yang kita inginkan karena kitasetvar
akan menetapkan global var yang bertentangan dengan pemanggil jika pemanggil adalah fungsi, seperti di:yang akan menghasilkan:
Juga, perhatikan bahwa sementara
declare
dipanggildeclare
(sebenarnyabash
meminjam konsep daritypeset
builtin shell Korn ), jika variabel sudah ditetapkan,declare
tidak mendeklarasikan variabel baru dan cara tugas dilakukan tergantung pada jenis variabel.Contohnya:
akan menghasilkan hasil yang berbeda (dan berpotensi memiliki efek samping buruk) jika
varname
sebelumnya dinyatakan sebagai skalar , array , atau array asosiatif .sumber
bash
tidak tersedia (atau ketika Anda menyadari bahwa Anda memerlukan shell yang lebih baik / lebih cepat). Theeval
karya sintaks dalam semua Bourne-seperti kerang dan POSIX sehingga semua sistem akan memilikish
mana yang bekerja. (itu juga berarti jawaban saya berlaku untuk semua shell, dan cepat atau lambat, seperti yang sering terjadi di sini, Anda akan melihat pertanyaan spesifik non-bash ditutup sebagai duplikat dari pertanyaan ini.$var_name
berisi token? ... suka;
?eval
dandeclare
(memikirkanPATH
,TMOUT
,PS4
,SECONDS
...).export
. saya tidak mengikuti sedikit tanda kurung pada akhirnya.Jika kamu melakukan:
... dan
$name
mengandung;
- atau salah satu dari beberapa token lain yang mungkin ditafsirkan oleh shell sebagai membatasi perintah sederhana - didahului dengan sintaksis shell yang tepat, yang akan dieksekusi.KELUARAN
Kadang-kadang dimungkinkan untuk memisahkan evaluasi dan pelaksanaan pernyataan tersebut. Misalnya,
alias
dapat digunakan untuk mengevaluasi dulu suatu perintah. Dalam contoh berikut, definisi variabel disimpan kealias
yang hanya dapat dinyatakan dengan sukses jika$nm
variabel yang dievaluasi tidak mengandung byte yang tidak cocok dengan alfanumerik ASCII atau_
.eval
digunakan di sini untuk menangani memohon yang barualias
dari varname. Tapi itu hanya dipanggil sama sekali jikaalias
definisi sebelumnya berhasil, dan sementara saya tahu banyak implementasi yang berbeda akan menerima banyak jenis nilai untukalias
nama, saya belum menemukan yang akan menerima yang benar-benar kosong .Namun, definisi di dalam
alias
adalah untuk_$nm
, dan ini untuk memastikan bahwa tidak ada nilai lingkungan signifikan yang dituliskan. Saya tidak tahu nilai lingkungan penting apa pun yang dimulai dengan a_
dan biasanya merupakan taruhan yang aman untuk deklarasi semi-pribadi.Lagi pula, jika
alias
definisi berhasil, itu akan mendeklarasikan nilaialias
untuk nama$nm
. Daneval
hanya akan memanggil itualias
jika juga tidak dimulai dengan angka - yang laineval
hanya mendapatkan argumen nol. Jadi, jika kedua kondisi terpenuhieval
panggilan alias dan definisi variabel disimpan dalam alias dibuat, setelah itu barualias
segera dihapus dari tabel hash.sumber
;
tidak diizinkan dalam nama variabel. Jika Anda tidak memiliki kendali atas konten$name
, maka Anda perlu membersihkannya untukexport
/declare
juga. Meskipunexport
tidak mengeksekusi kode, mengatur beberapa variabel sukaPATH
,PS4
dan banyak dari mereka yanginfo -f bash -n 'Bash Variables'
memiliki efek samping yang sama-sama berbahaya.eval
pass pertama - ini adalah ekspansi variabel. Seperti yang Anda katakan di tempat lain, dalam konteks itu sangat diperbolehkan. Argumen $ PATH masih sangat bagus - saya membuat sedikit edit dan akan menambahkan beberapa nanti.zsh
,pdksh
,mksh
,yash
jangan mengeluh padaunset 'a;b'
.unset -v -- ...
juga.