eval
dan exec
keduanya dibangun di perintah bash (1) yang menjalankan perintah.
Saya juga melihat exec
ada beberapa opsi tetapi apakah itu satu-satunya perbedaan? Apa yang terjadi pada konteks mereka?
eval
dan exec
keduanya dibangun di perintah bash (1) yang menjalankan perintah.
Saya juga melihat exec
ada beberapa opsi tetapi apakah itu satu-satunya perbedaan? Apa yang terjadi pada konteks mereka?
Jawaban:
eval
dan exec
binatang yang sama sekali berbeda. (Terlepas dari kenyataan bahwa keduanya akan menjalankan perintah, tetapi demikian juga semua yang Anda lakukan dalam sebuah shell.)
$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
Replace the shell with the given command.
Apa yang exec cmd
dilakukan, persis sama dengan hanya menjalankan cmd
, kecuali bahwa shell saat ini diganti dengan perintah, alih-alih proses terpisah dijalankan. Secara internal, running say /bin/ls
akan memanggil fork()
untuk membuat proses anak, dan kemudian exec()
pada anak untuk mengeksekusi /bin/ls
. exec /bin/ls
di sisi lain tidak akan bercabang, tetapi hanya mengganti shell.
Membandingkan:
$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo
dengan
$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217
echo $$
mencetak PID dari shell yang saya mulai, dan listing /proc/self
memberi kita PID dari ls
yang dijalankan dari shell. Biasanya, ID proses berbeda, tetapi dengan exec
shell dan ls
memiliki ID proses yang sama. Juga, perintah berikut exec
tidak berjalan, karena shell diganti.
Di samping itu:
$ help eval
eval: eval [arg ...]
Execute arguments as a shell command.
eval
akan menjalankan argumen sebagai perintah di shell saat ini. Dengan kata lain eval foo bar
sama dengan adil foo bar
. Tetapi variabel akan diperluas sebelum dieksekusi, sehingga kami dapat mengeksekusi perintah yang disimpan dalam variabel shell:
$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo
Itu tidak akan membuat proses anak, sehingga variabel diatur dalam shell saat ini. (Tentu saja eval /bin/ls
akan menciptakan proses anak, seperti yang dilakukan oleh orang tua biasa /bin/ls
.)
Atau kita bisa memiliki perintah yang menampilkan perintah shell. Menjalankan ssh-agent
memulai agen di latar belakang, dan menghasilkan banyak tugas variabel, yang dapat diatur dalam shell saat ini dan digunakan oleh proses anak ( ssh
perintah yang akan Anda jalankan). Maka ssh-agent
dapat dimulai dengan:
eval $(ssh-agent)
Dan shell saat ini akan mendapatkan variabel untuk mewarisi perintah lain.
Tentu saja, jika variabel tersebut cmd
mengandung sesuatu seperti rm -rf $HOME
, maka menjalankan eval "$cmd"
bukan sesuatu yang ingin Anda lakukan. Bahkan hal-hal seperti penggantian perintah di dalam string akan diproses, jadi orang harus benar - benar yakin bahwa input ke eval
aman sebelum menggunakannya.
Seringkali, mungkin untuk menghindari eval
dan menghindari bahkan secara tidak sengaja mencampur kode dan data dengan cara yang salah.
eval
di tempat pertama untuk jawaban ini juga. Hal-hal seperti memodifikasi variabel secara tidak langsung dapat dilakukan di banyak shell melalui declare
/ typeset
/ nameref
dan ekspansi seperti ${!var}
, jadi saya akan menggunakan itu alih-alih eval
kecuali saya benar-benar harus menghindarinya.
exec
tidak membuat proses baru. Ini menggantikan proses saat ini dengan perintah baru. Jika Anda melakukan ini pada baris perintah maka itu akan secara efektif mengakhiri sesi shell Anda (dan mungkin mengeluarkan Anda atau menutup jendela terminal!)
misalnya
ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh%
Di sini saya di ksh
(shell normal saya). Aku mulai bash
dan kemudian di dalam bash Saya exec /bin/echo
. Kita dapat melihat bahwa saya telah dikembalikan ke ksh
setelah itu karena bash
prosesnya diganti oleh /bin/echo
.
exec
digunakan untuk mengganti proses shell saat ini dengan yang baru dan menangani aliran redirection / deskriptor file jika tidak ada perintah yang ditentukan. eval
digunakan untuk mengevaluasi string sebagai perintah. Keduanya dapat digunakan untuk membangun dan mengeksekusi perintah dengan argumen yang dikenal saat run-time, tetapi exec
menggantikan proses shell saat ini di samping mengeksekusi perintah.
Sintaksis:
exec [-cl] [-a name] [command [arguments]]
Menurut manual jika ada perintah yang ditentukan ini built-in
... mengganti shell. Tidak ada proses baru yang dibuat. Argumen menjadi argumen untuk perintah.
Dengan kata lain, jika Anda menjalankan bash
dengan PID 1234 dan jika Anda menjalankan di exec top -u root
dalam shell itu, top
perintah kemudian akan memiliki PID 1234 dan mengganti proses shell Anda.
Di mana ini berguna? Dalam sesuatu yang dikenal sebagai skrip wrapper. Script tersebut membangun kumpulan argumen atau membuat keputusan tertentu tentang variabel apa yang harus dilewatkan ke lingkungan, dan kemudian digunakan exec
untuk menggantikan dirinya dengan perintah apa pun yang ditentukan, dan tentu saja memberikan argumen yang sama dengan yang dibuat oleh skrip wrapper di sepanjang jalan.
Apa yang manual juga nyatakan adalah:
Jika perintah tidak ditentukan, pengalihan apa pun akan berlaku di shell saat ini
Ini memungkinkan kita untuk mengarahkan ulang apa pun dari arus keluaran shells ke file. Ini mungkin berguna untuk tujuan logging atau penyaringan, di mana Anda tidak ingin melihat stdout
perintah tetapi hanya stderr
. Misalnya, seperti:
bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt
2017年 05月 20日 星期六 05:01:51 MDT
HELLO WORLD
Perilaku ini membuatnya mudah untuk masuk dalam skrip shell , mengarahkan aliran untuk memisahkan file atau proses , dan hal - hal menyenangkan lainnya dengan deskriptor file.
Pada level kode sumber setidaknya untuk bash
versi 4.3, exec
built-in didefinisikan dalam builtins/exec.def
. Ini mem-parsing perintah yang diterima, dan jika ada, itu meneruskan hal-hal ke shell_execve()
fungsi yang didefinisikan dalam execute_cmd.c
file.
Singkatnya, ada keluarga exec
perintah dalam bahasa pemrograman C, dan shell_execve()
pada dasarnya adalah fungsi pembungkus dari execve
:
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
Manual bash 4.3 menyatakan (penekanan ditambahkan oleh saya):
Args dibaca dan disatukan menjadi satu perintah. Perintah ini kemudian dibaca dan dieksekusi oleh shell , dan status keluarnya dikembalikan sebagai nilai eval.
Perhatikan bahwa tidak ada proses penggantian yang terjadi. Tidak seperti di exec
mana tujuannya adalah untuk mensimulasikan execve()
fungsionalitas, eval
built in hanya berfungsi untuk "mengevaluasi" argumen, sama seperti jika pengguna telah mengetiknya pada baris perintah. Dengan demikian, proses baru dibuat.
Di mana ini mungkin berguna? Seperti yang ditunjukkan Gilles dalam jawaban ini , "... eval tidak sering digunakan. Dalam beberapa shell, penggunaan yang paling umum adalah untuk mendapatkan nilai dari variabel yang namanya tidak dikenal sampai runtime". Secara pribadi, saya telah menggunakannya dalam beberapa skrip di Ubuntu di mana perlu untuk mengeksekusi / mengevaluasi perintah berdasarkan ruang kerja spesifik yang saat ini digunakan pengguna.
Pada level kode sumber, ia didefinisikan di dalam builtins/eval.def
dan meneruskan string input yang diuraikan untuk evalstring()
berfungsi.
Antara lain, eval
dapat menetapkan variabel yang tetap dalam lingkungan eksekusi shell saat ini, sementara exec
tidak dapat:
$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
membuat proses anak baru, menjalankan argumen dan mengembalikan status keluar.
Uh apa? Intinya eval
adalah bahwa ia tidak dengan cara apa pun menciptakan proses anak. Jika aku melakukan
eval "cd /tmp"
dalam sebuah shell, lalu setelah itu shell yang sekarang akan berubah direktori. Tidak exec
membuat proses anak baru, melainkan mengubah executable saat ini (yaitu shell) untuk yang diberikan; id proses (dan buka file dan hal-hal lain) tetap sama. Berbeda dengan eval
, sebuah exec
tidak akan kembali ke shell panggilan kecuali exec
itu sendiri gagal karena tidak dapat menemukan atau memuat executable atau mati untuk masalah ekspansi argumen.
eval
pada dasarnya mengartikan argumennya sebagai string setelah concatenation, yaitu ia akan melakukan lapisan tambahan ekspansi wildcard dan pemisahan argumen. exec
tidak melakukan hal seperti itu.
Evaluasi
Pekerjaan ini:
$ echo hi
hi
$ eval "echo hi"
hi
$ exec echo hi
hi
Namun, ini tidak:
$ exec "echo hi"
bash: exec: echo hi: not found
$ "echo hi"
bash: echo hi: command not found
Memproses penggantian gambar
Contoh ini menunjukkan bagaimana exec
mengganti gambar proses pemanggilannya:
# Get PID of current shell
sh$ echo $$
1234
# Enter a subshell with PID 5678
sh$ sh
# Check PID of subshell
sh-subshell$ echo $$
5678
# Run exec
sh-subshell$ exec echo $$
5678
# We are back in our original shell!
sh$ echo $$
1234
Perhatikan bahwa exec echo $$
dijalankan dengan PID dari subkulit! Selanjutnya, setelah selesai, kami kembali ke sh$
cangkang asli kami .
Di sisi lain, eval
tidak bukan mengganti gambar proses. Sebaliknya, ia menjalankan perintah yang diberikan seperti yang biasanya Anda lakukan di dalam shell itu sendiri. (Tentu saja, jika Anda menjalankan perintah yang memerlukan proses untuk melahirkan ... itu tidak hanya itu!)
sh$ echo $$
1234
sh$ sh
sh-subshell$ echo $$
5678
sh-subshell$ eval echo $$
5678
# We are still in the subshell!
sh-subshell$ echo $$
5678
exec
)