Setelah membaca halaman bash man dan sehubungan dengan posting ini .
Saya masih kesulitan memahami apa yang sebenarnya dilakukan eval
perintah dan yang akan menjadi kegunaan khasnya. Sebagai contoh jika kita lakukan:
bash$ set -- one two three # sets $1 $2 $3
bash$ echo $1
one
bash$ n=1
bash$ echo ${$n} ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
bash$ echo $($n) ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
bash$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one
Apa sebenarnya yang terjadi di sini dan bagaimana tanda dolar dan garis miring terbalik menjadi masalah?
$($n)
berjalan$n
dalam subkulit. Mencoba menjalankan perintah1
yang tidak ada.echo $1
akhirnya, bukan1
. Saya tidak berpikir itu bisa dilakukan menggunakan subkulit.eval
.Jawaban:
eval
mengambil string sebagai argumennya, dan mengevaluasinya seolah-olah Anda mengetik string itu pada baris perintah. (Jika Anda melewati beberapa argumen, mereka pertama kali bergabung dengan spasi di antara mereka.)${$n}
adalah kesalahan sintaks dalam bash. Di dalam kurung kurawal, Anda hanya dapat memiliki nama variabel, dengan beberapa kemungkinan awalan dan sufiks, tetapi Anda tidak dapat memiliki sintaks bash sembarang dan khususnya Anda tidak dapat menggunakan ekspansi variabel. Ada cara untuk mengatakan "nilai variabel yang namanya ada dalam variabel ini", meskipun:$(…)
menjalankan perintah yang ditentukan di dalam tanda kurung dalam sebuah subkulit (yaitu dalam proses terpisah yang mewarisi semua pengaturan seperti nilai variabel dari shell saat ini), dan mengumpulkan hasilnya. Jadiecho $($n)
berjalan$n
sebagai perintah shell, dan menampilkan hasilnya. Sejak$n
dievaluasi untuk1
,$($n)
upaya untuk menjalankan perintah1
, yang tidak ada.eval echo \${$n}
menjalankan parameter yang diteruskan keeval
. Setelah ekspansi, parameternya adalahecho
dan${1}
. Jadieval echo \${$n}
jalankan perintahecho ${1}
.Perhatikan bahwa sebagian besar waktu, Anda harus menggunakan tanda kutip ganda sekitar substitusi variabel dan substitusi perintah (yaitu kapan ada
$
):"$foo", "$(foo)"
. Selalu letakkan tanda kutip ganda di sekitar penggantian variabel dan perintah , kecuali Anda tahu Anda harus membiarkannya. Tanpa tanda kutip ganda, shell melakukan pemisahan bidang (yaitu membagi nilai variabel atau output dari perintah menjadi kata-kata yang terpisah) dan kemudian memperlakukan setiap kata sebagai pola wildcard. Sebagai contoh:eval
tidak sering digunakan. Dalam beberapa shell, penggunaan yang paling umum adalah untuk mendapatkan nilai dari variabel yang namanya tidak diketahui sampai runtime. Dalam bash, ini tidak perlu berkat${!VAR}
sintaksnya.eval
masih berguna ketika Anda perlu membangun perintah yang lebih panjang yang berisi operator, kata-kata yang dicadangkan, dll.sumber
eval
menerima string (yang mungkin merupakan hasil parsing dan evaluasi), dan menafsirkannya sebagai potongan kode.eval eval
. Saya tidak ingat pernah merasakan kebutuhan itu. Jika Anda benar-benar membutuhkan duaeval
berlalu, menggunakan variabel sementara, itu akan lebih mudah untuk debug:eval tmp="\${$i}"; eval x="\${$tmp}"
.eval
hanya mengambil kode dalam sebuah string dan mengevaluasinya sesuai dengan aturan yang biasa. Secara teknis, itu bahkan tidak benar, karena ada beberapa kasus sudut di mana Bash memodifikasi parsing untuk bahkan tidak melakukan ekspansi ke argumen eval - tapi itu adalah berita gembira yang sangat tidak jelas yang saya ragu ada yang tahu.Cukup anggap eval sebagai "mengevaluasi ekspresi Anda satu kali tambahan sebelum eksekusi"
eval echo \${$n}
menjadiecho $1
setelah putaran pertama evaluasi. Tiga perubahan untuk diperhatikan:\$
menjadi$
(garis miring terbalik diperlukan, jika tidak ia mencoba untuk mengevaluasi${$n}
, yang berarti variabel bernama{$n}
, yang tidak diperbolehkan)$n
dievaluasi untuk1
eval
hilangDi babak kedua, pada dasarnya
echo $1
yang bisa langsung dieksekusi.Jadi
eval <some command>
pertama-tama akan mengevaluasi<some command>
(dengan mengevaluasi di sini saya maksudkan variabel pengganti, ganti karakter yang keluar dengan yang benar, dll.), Dan kemudian jalankan ekspresi yang dihasilkan sekali lagi.eval
digunakan ketika Anda ingin secara dinamis membuat variabel, atau membaca keluaran dari program yang dirancang khusus untuk dibaca seperti ini. Lihat http://mywiki.wooledge.org/BashFAQ/048 untuk contohnya. Tautan ini juga berisi beberapa cara khas yangeval
digunakan, dan risiko yang terkait dengannya.sumber
${VAR}
sintaks yang diperbolehkan, dan lebih suka ketika ada ambiguitas (tidak$VAR == $V
, diikuti olehAR
atau$VAR == $VA
diikuti olehR
).${VAR}
setara dengan$VAR
. Bahkan, itu nama variabel$n
yang tidak diizinkan.eval eval echo \\\${\${$i}}
akan melakukan dereferensi tiga. Saya tidak yakin apakah ada cara yang lebih sederhana untuk melakukan itu. Juga,\${$n}
bekerja dengan baik (cetakanone
) pada mesin saya ..echo \\\${\${$i}}
mencetak\${${n}}
.eval echo \\\${\${$i}}
sama denganecho \${${n}}`` and prints
$ {1}.
eval eval echo \\ $ {\ $ {$ i}} `setara denganeval echo ${1}
dan dicetakone
.` escapes the second one, and the third
`lolos$
setelah itu. Jadi itu menjadi\${${n}}
setelah satu putaran evaluasi\\
menghasilkan satu backslash. Kemudian\$
menghasilkan satu dolar. Dan seterusnya.Dalam pengalaman saya, penggunaan eval "tipikal" adalah untuk menjalankan perintah yang menghasilkan perintah shell untuk mengatur variabel lingkungan.
Mungkin Anda memiliki sistem yang menggunakan kumpulan variabel lingkungan, dan Anda memiliki skrip atau program yang menentukan mana yang harus ditetapkan dan nilainya. Setiap kali Anda menjalankan skrip atau program, ia berjalan dalam proses bercabang, jadi apa pun yang dilakukannya secara langsung ke variabel lingkungan akan hilang saat keluar. Tetapi skrip atau program itu dapat mengirim perintah ekspor ke stdout.
Tanpa eval, Anda perlu mengarahkan stdout ke file temp, sumber file temp, dan kemudian menghapusnya. Dengan eval, Anda bisa:
Perhatikan kutipan itu penting. Ambil contoh ini (buat-buat):
sumber
${varname}
. Saya merasa nyaman untuk menggunakan file .conf yang identik pada beberapa server yang berbeda dengan hanya beberapa hal yang diparameterisasi oleh variabel lingkungan. Saya mengedit / opt / lampp / xampp (yang memulai apache) untuk melakukan eval semacam ini dengan skrip yang menyembul di sekitar sistem dan menampilkanexport
pernyataan bash untuk mendefinisikan variabel untuk file .conf.eval "$(pyenv init -)"
ke dalam.bash_profile
(atau serupa) shell file konfigurasi. Itu membangun skrip shell kecil kemudian mengevaluasinya di shell saat ini.Pernyataan eval memberitahu shell untuk mengambil argumen eval sebagai perintah dan menjalankannya melalui command-line. Ini berguna dalam situasi seperti di bawah ini:
Dalam skrip Anda jika Anda mendefinisikan perintah menjadi variabel dan kemudian Anda ingin menggunakan perintah itu maka Anda harus menggunakan eval:
sumber
Pembaruan: Beberapa orang mengatakan seseorang harus-tidak pernah menggunakan eval. Saya tidak setuju. Saya pikir risikonya muncul ketika input yang korup dapat diteruskan
eval
. Namun ada banyak situasi umum di mana itu bukan risiko, dan karena itu perlu diketahui bagaimana menggunakan eval dalam hal apa pun. Jawaban stackoverflow ini menjelaskan risiko eval dan alternatif eval. Pada akhirnya tergantung pada pengguna untuk menentukan apakah eval aman dan efisien untuk digunakan.eval
Pernyataan bash memungkinkan Anda untuk mengeksekusi baris kode yang dihitung atau diperoleh, dengan skrip bash Anda.Mungkin contoh yang paling mudah adalah program bash yang membuka skrip bash lain sebagai file teks, membaca setiap baris teks, dan menggunakan
eval
untuk mengeksekusinya secara berurutan. Itu pada dasarnya perilaku yang sama dengansource
pernyataan bash , yang akan digunakan orang, kecuali jika diperlukan untuk melakukan semacam transformasi (misalnya penyaringan atau substitusi) pada konten skrip yang diimpor.Saya jarang membutuhkannya
eval
, tetapi saya merasa berguna untuk membaca atau menulis variabel yang namanya terkandung dalam string yang ditugaskan ke variabel lain. Misalnya, untuk melakukan tindakan pada set variabel, sambil menjaga kode jejak kecil dan menghindari redundansi.eval
secara konseptual sederhana. Namun, sintaks yang ketat dari bahasa bash, dan urutan parsing penerjemah bash dapat diberi nuansa dan membuatnyaeval
tampak samar dan sulit untuk digunakan atau dipahami. Berikut ini yang penting:Argumen yang diteruskan ke
eval
adalah ekspresi string yang dihitung pada saat runtime.eval
akan mengeksekusi hasil parsing terakhir dari argumennya sebagai baris kode aktual dalam skrip Anda.Sintaks dan urutan parsing ketat. Jika hasilnya bukan baris kode bash yang dapat dieksekusi, dalam lingkup skrip Anda, program akan macet pada
eval
pernyataan saat mencoba mengeksekusi sampah.Saat menguji Anda dapat mengganti
eval
pernyataan denganecho
dan melihat apa yang ditampilkan. Jika itu kode yang sah dalam konteks saat ini, menjalankannyaeval
akan berhasil.Contoh-contoh berikut dapat membantu menjelaskan bagaimana eval bekerja ...
Contoh 1:
eval
pernyataan di depan kode 'normal' adalah NOPDalam contoh di atas,
eval
pernyataan pertama tidak memiliki tujuan dan dapat dihilangkan.eval
tidak ada gunanya di baris pertama karena tidak ada aspek dinamis untuk kode, yaitu sudah diuraikan ke dalam baris akhir kode bash, sehingga akan sama dengan pernyataan kode normal dalam skrip bash. -2eval
ada gunanya juga, karena, meskipun ada langkah parsing mengkonversi$a
ke setara string literal nya, tidak ada tipuan (misalnya tidak ada referensi melalui nilai string dari yang sebenarnya benda bash atau variabel bash script-diadakan), sehingga akan berperilaku identik sebagai baris kode tanpaeval
awalan.Contoh 2:
Melakukan penugasan var menggunakan nama var yang diteruskan sebagai nilai string.
Jika Anda melakukannya
echo $key=$val
, hasilnya adalah:Bahwa , sebagai hasil akhir dari penguraian string, adalah apa yang akan dieksekusi oleh eval, maka hasil dari pernyataan echo di akhir ...
Contoh 3:
Menambahkan lebih banyak tipuan ke Contoh 2
Di atas sedikit lebih rumit dari contoh sebelumnya, lebih bergantung pada urutan parsing dan kekhasan bash. The
eval
garis akan kira-kira bisa diurai secara internal dalam urutan sebagai berikut (perhatikan pernyataan berikut pseudocode, bukan kode nyata, hanya untuk mencoba untuk menunjukkan bagaimana pernyataan akan mendapatkan dipecah menjadi langkah-langkah internal untuk sampai pada hasil akhir) .Jika asumsi penguraian tidak menjelaskan apa yang dilakukan eval cukup, contoh ketiga dapat menjelaskan penguraian lebih detail untuk membantu memperjelas apa yang sedang terjadi.
Contoh 4:
Temukan apakah vars, yang namanya terkandung dalam string, sendiri mengandung nilai string.
Dalam iterasi pertama:
Bash mem-parsing argumen ke
eval
, daneval
melihat ini secara harfiah pada saat runtime:Pseudocode berikut ini mencoba menggambarkan bagaimana bash menginterpretasikan baris kode real di atas , untuk sampai pada nilai akhir yang dieksekusi oleh
eval
. (baris berikut deskriptif, bukan kode bash yang tepat):Setelah semua penguraian selesai, hasilnya adalah apa yang dieksekusi, dan efeknya jelas, menunjukkan tidak ada yang misterius tentang
eval
dirinya sendiri, dan kompleksitasnya adalah penguraian argumennya.Kode yang tersisa dalam contoh di atas hanya menguji untuk melihat apakah nilai yang ditetapkan untuk $ varval adalah nol, dan, jika demikian, meminta pengguna untuk memberikan nilai.
sumber
Awalnya saya sengaja tidak pernah belajar cara menggunakan eval, karena kebanyakan orang akan merekomendasikan untuk menjauh darinya seperti wabah. Namun saya baru-baru ini menemukan kasus penggunaan yang membuat saya facepalm untuk tidak mengenalinya lebih cepat.
Jika Anda memiliki pekerjaan cron yang ingin Anda jalankan secara interaktif untuk diuji, Anda mungkin melihat konten file dengan cat, dan menyalin dan menempelkan pekerjaan cron untuk menjalankannya. Sayangnya, ini melibatkan menyentuh mouse, yang merupakan dosa dalam buku saya.
Katakanlah Anda memiliki pekerjaan cron di /etc/cron.d/repeatme dengan konten:
*/10 * * * * root program arg1 arg2
Anda tidak dapat menjalankan ini sebagai skrip dengan semua sampah di depannya, tetapi kita dapat menggunakan cut untuk menyingkirkan semua sampah, membungkusnya dalam sebuah subkulit, dan mengeksekusi string dengan eval
eval $( cut -d ' ' -f 6- /etc/cron.d/repeatme)
Perintah cut hanya mencetak bidang ke-6 file, dibatasi oleh spasi. Eval kemudian menjalankan perintah itu.
Saya menggunakan cron job di sini sebagai contoh, tetapi konsepnya adalah memformat teks dari stdout, dan kemudian mengevaluasi teks itu.
Penggunaan eval dalam hal ini tidak aman, karena kita tahu persis apa yang akan kita evaluasi sebelumnya.
sumber
Saya baru-baru ini harus menggunakan
eval
untuk memaksa beberapa ekspansi brace dievaluasi dalam urutan yang saya butuhkan. Bash melakukan beberapa ekspansi brace dari kiri ke kanan, jadimeluas ke
tapi saya perlu ekspansi brace kedua dilakukan pertama, menghasilkan
Yang terbaik yang bisa saya lakukan untuk melakukan itu adalah
Ini bekerja karena tanda kutip tunggal melindungi set kawat gigi pertama dari ekspansi selama penguraian
eval
baris perintah, meninggalkannya untuk diperluas oleh subkulit yang dipanggil oleheval
.Mungkin ada beberapa skema licik yang melibatkan ekspansi penjepit bersarang yang memungkinkan ini terjadi dalam satu langkah, tetapi jika ada saya terlalu tua dan bodoh untuk melihatnya.
sumber
Anda bertanya tentang kegunaan umum.
Satu keluhan umum tentang skrip shell adalah bahwa Anda (diduga) tidak dapat melewati referensi untuk mendapatkan nilai kembali dari fungsi.
Tetapi sebenarnya, melalui "eval", Anda dapat melewati referensi. Callee dapat mengirimkan kembali daftar tugas variabel untuk dievaluasi oleh penelepon. Ini lewat referensi karena penelepon dapat diizinkan untuk menentukan nama variabel hasil - lihat contoh di bawah ini. Hasil kesalahan dapat diberikan kembali nama standar seperti errno dan errstr.
Berikut ini adalah contoh lewat referensi di bash:
Outputnya terlihat seperti ini:
Ada lebar pita hampir tak terbatas dalam output teks itu! Dan ada lebih banyak kemungkinan jika garis keluaran berganda digunakan: misalnya, baris pertama dapat digunakan untuk penugasan variabel, yang kedua untuk 'aliran pemikiran' terus menerus, tapi itu di luar cakupan tulisan ini.
sumber
Saya suka jawaban "mengevaluasi ekspresi Anda satu kali sebelum eksekusi", dan ingin menjelaskan dengan contoh lain.
Hasil Penasaran di Opsi 2 adalah bahwa kami telah melewati 2 parameter sebagai berikut:
"value
content"
Bagaimana itu untuk kontra intuitif? Tambahan
eval
akan memperbaikinya.Diadaptasi dari https://stackoverflow.com/a/40646371/744133
sumber
Dalam pertanyaan:
kesalahan output mengklaim bahwa file a dan tty tidak ada. Saya mengerti ini berarti bahwa tty tidak ditafsirkan sebelum eksekusi grep, tetapi sebaliknya bash meneruskan tty sebagai parameter untuk grep, yang menafsirkannya sebagai nama file.
Ada juga situasi pengalihan bersarang, yang harus ditangani oleh tanda kurung yang cocok yang harus menentukan proses anak, tetapi bash pada dasarnya adalah pemisah kata, membuat parameter untuk dikirim ke suatu program, oleh karena itu tanda kurung tidak cocok terlebih dahulu, tetapi ditafsirkan sebagai terlihat.
Saya mendapatkan spesifik dengan grep, dan menetapkan file sebagai parameter alih-alih menggunakan pipa. Saya juga menyederhanakan perintah dasar, meneruskan keluaran dari perintah sebagai file, sehingga i / o piping tidak akan disarangkan:
bekerja dengan baik.
tidak benar-benar diinginkan, tetapi menghilangkan pipa bersarang dan juga berfungsi dengan baik.
Kesimpulannya, bash tampaknya tidak suka pipping bersarang. Penting untuk dipahami bahwa bash bukanlah program gelombang baru yang ditulis secara rekursif. Sebaliknya, bash adalah program lama 1,2,3, yang telah ditambahkan dengan fitur. Untuk tujuan memastikan kompatibilitas ke belakang, cara penafsiran awal tidak pernah dimodifikasi. Jika bash ditulis ulang untuk mencocokkan tanda kurung pertama, berapa banyak bug yang akan diperkenalkan ke dalam berapa banyak program bash? Banyak programmer suka menjadi samar.
sumber