Apa perbedaan antara eval dan eksekutif?

Jawaban:

124

evaldan execbinatang 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 cmddilakukan, persis sama dengan hanya menjalankan cmd, kecuali bahwa shell saat ini diganti dengan perintah, alih-alih proses terpisah dijalankan. Secara internal, running say /bin/lsakan memanggil fork()untuk membuat proses anak, dan kemudian exec()pada anak untuk mengeksekusi /bin/ls. exec /bin/lsdi 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/selfmemberi kita PID dari lsyang dijalankan dari shell. Biasanya, ID proses berbeda, tetapi dengan execshell dan lsmemiliki ID proses yang sama. Juga, perintah berikut exectidak berjalan, karena shell diganti.


Di samping itu:

$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

evalakan menjalankan argumen sebagai perintah di shell saat ini. Dengan kata lain eval foo barsama 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/lsakan menciptakan proses anak, seperti yang dilakukan oleh orang tua biasa /bin/ls.)

Atau kita bisa memiliki perintah yang menampilkan perintah shell. Menjalankan ssh-agentmemulai agen di latar belakang, dan menghasilkan banyak tugas variabel, yang dapat diatur dalam shell saat ini dan digunakan oleh proses anak ( sshperintah yang akan Anda jalankan). Maka ssh-agentdapat dimulai dengan:

eval $(ssh-agent)

Dan shell saat ini akan mendapatkan variabel untuk mewarisi perintah lain.


Tentu saja, jika variabel tersebut cmdmengandung 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 evalaman sebelum menggunakannya.

Seringkali, mungkin untuk menghindari evaldan menghindari bahkan secara tidak sengaja mencampur kode dan data dengan cara yang salah.

ilkkachu
sumber
Itu jawaban yang bagus, @ilkkachu. Terima kasih!
Willian Paixao
Rincian lebih lanjut tentang penggunaan eval dapat ditemukan di sini: stackoverflow.com/a/46944004/2079103
clearlight
1
@clearlight, baik, yang mengingatkan saya untuk menambahkan disclaimer biasa tentang tidak menggunakan evaldi tempat pertama untuk jawaban ini juga. Hal-hal seperti memodifikasi variabel secara tidak langsung dapat dilakukan di banyak shell melalui declare/ typeset/ namerefdan ekspansi seperti ${!var}, jadi saya akan menggunakan itu alih-alih evalkecuali saya benar-benar harus menghindarinya.
ilkkachu
27

exectidak 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 bashdan kemudian di dalam bash Saya exec /bin/echo. Kita dapat melihat bahwa saya telah dikembalikan ke kshsetelah itu karena bashprosesnya diganti oleh /bin/echo.

Stephen Harris
sumber
umm pada wajah turun kembali ke proses ksh b / c digantikan oleh gema tidak masuk akal?
14

TL; DR

execdigunakan untuk mengganti proses shell saat ini dengan yang baru dan menangani aliran redirection / deskriptor file jika tidak ada perintah yang ditentukan. evaldigunakan untuk mengevaluasi string sebagai perintah. Keduanya dapat digunakan untuk membangun dan mengeksekusi perintah dengan argumen yang dikenal saat run-time, tetapi execmenggantikan proses shell saat ini di samping mengeksekusi perintah.

exec buil-in

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 bashdengan PID 1234 dan jika Anda menjalankan di exec top -u rootdalam shell itu, topperintah 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 execuntuk 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 stdoutperintah 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 bashversi 4.3, execbuilt-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.cfile.

Singkatnya, ada keluarga execperintah 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;
{

eval built-in

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 execmana tujuannya adalah untuk mensimulasikan execve()fungsionalitas, evalbuilt 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.defdan meneruskan string input yang diuraikan untuk evalstring()berfungsi.

Antara lain, evaldapat menetapkan variabel yang tetap dalam lingkungan eksekusi shell saat ini, sementara exectidak dapat:

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
Sergiy Kolodyazhnyy
sumber
@ ctrl-alt-delor Saya telah mengedit bagian itu, terima kasih, meskipun bisa dibilang itu memunculkan proses baru, PID hanya tetap sama dengan shell saat ini. Di masa depan pertimbangkan untuk mengedit jawaban, alih-alih meninggalkan komentar dan downvote, terutama ketika hal sepele seperti frase 7-kata dipertanyakan; jauh lebih sedikit waktu untuk mengedit dan memperbaiki jawaban, dan itu jauh lebih bermanfaat.
Sergiy Kolodyazhnyy
5
membuat proses anak baru, menjalankan argumen dan mengembalikan status keluar.

Uh apa? Intinya evaladalah 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 execmembuat 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 exectidak akan kembali ke shell panggilan kecuali execitu sendiri gagal karena tidak dapat menemukan atau memuat executable atau mati untuk masalah ekspansi argumen.

evalpada dasarnya mengartikan argumennya sebagai string setelah concatenation, yaitu ia akan melakukan lapisan tambahan ekspansi wildcard dan pemisahan argumen. exectidak melakukan hal seperti itu.

pengguna180452
sumber
1
Pertanyaan aslinya berbunyi "eval dan exec keduanya dibangun di perintah Bash (1) dan mengeksekusi perintah, menciptakan proses anak baru, menjalankan argumen dan mengembalikan status keluar"; Saya telah mengedit anggapan yang salah.
Charles Stewart
-1

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 execmengganti 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, evaltidak 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
Mateen Ulhaq
sumber
Saya memposting jawaban ini karena contoh-contoh lain tidak benar-benar menggambarkan hal ini dalam cara yang cukup dimengerti untuk pikiran lemah seperti saya.
Mateen Ulhaq
Pilihan kata yang buruk: Substitusi proses adalah konsep yang sudah ada yang tampaknya tidak terkait dengan apa pun yang Anda ilustrasikan di sini.
muru
@muru Apakah "proses penggantian" lebih baik? Saya tidak yakin apa terminologi yang benar. Halaman manual tampaknya menyebutnya "mengganti gambar proses".
Mateen Ulhaq
Hmm, tidak tahu. (Tapi ketika saya mendengar "menggantikan proses image", saya pikir: exec)
Muru