Eval ve exec arasındaki fark nedir?


Yanıtlar:


124

evalve exectamamen farklı hayvanlar. (Her ikisinin de komutları çalıştıracağı gerçeğinden ayrı olarak, ancak kabuğunuzda yaptığınız her şeyi yapar.)

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

Ne var exec cmdki, sadece çalışan ile tam olarak aynıdır cmd, ancak mevcut kabuğun ayrı bir işlem yerine komutla değiştirilmesi gerekir. Dahili olarak, çalışan say /bin/ls, fork()bir çocuk süreci oluşturmaya ve daha sonra exec()yürütmek için çocuğa çağrı yapacak /bin/ls. exec /bin/lsÖte yandan, çatal olmaz , ama sadece kabuk değiştirir.

Karşılaştırmak:

$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo

ile

$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217

echo $$Başlattığım kabuğun PID'ini yazdırıyor ve listeleme /proc/selfbize lskabuğun koştuğu PID'yi veriyor . Genellikle işlem kimlikleri farklıdır, ancak execkabukla lsaynı işlem kimliğine sahiptir. Ayrıca, execkabuk değiştirildiğinden beri aşağıdaki komut çalışmadı.


Diğer yandan:

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

evalargümanları geçerli kabukta bir komut olarak çalıştıracaktır. Başka bir deyişle eval foo bar, sadece aynıdır foo bar. Fakat değişkenler çalıştırılmadan önce genişleyecektir, böylece kabuk değişkenlerine kaydedilen komutları çalıştırabiliriz:

$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo

Bu olacak değil değişken akım kabuk ayarlanır böylece, bir alt süreç oluşturmak. (Tabii ki eval /bin/lsbir çocuk süreci yaratacak, aynı düz bir yaşlı /bin/lsolacağı gibi.)

Veya kabuk komutları veren bir komut alabiliriz. Koşma ssh-agent, aracı arka planda başlatır ve alt kabukta ayarlanabilen ve alt işlemler ( sshçalıştırdığınız komutlar) tarafından kullanılabilecek bir dizi değişken ataması yapar . Dolayısıyla ssh-agentşu şekilde başlatılabilir:

eval $(ssh-agent)

Ve mevcut kabuk, diğer komutların devraldığı değişkenleri alacaktır.


Elbette, değişken bir cmdşey içeriyorsa rm -rf $HOME, o zaman koşmak eval "$cmd"istediğiniz bir şey olmaz. Böylece dize içinde komut değiştirmelerin gibi bile şeyler, işlenmiş olacağını kimse gerektiğini gerçekten giriş için emin olun evalkullanmadan önce güvenlidir.

Genellikle, evalkod ve verileri yanlışlıkla karıştırmanın ve hatta yanlış şekilde karıştırmanın önüne geçmek mümkündür .


Bu harika bir cevap, @ilkkachu. Teşekkürler!
Willian Paixao

Eval

1
@clearlight, bu bana ilk cevapta kullanmamaeval konusundaki olağan feragatnameyi de bu cevaba eklemeyi hatırlattı . Dolaylı olarak değiştirici değişkenler gibi şeyler , declare/ typeset/ namerefve benzeri gibi birçok kabukta yapılabilir ${!var}, bu yüzden onlardan evalkaçınmak zorunda olmadıkça, yerine bunları kullanırdım .
ilkkachu

27

execyeni bir işlem oluşturmaz. Bu değiştirir yeni komut ile şimdiki süreç. Bunu komut satırında yaptıysanız, kabuk oturumunuzu etkin bir şekilde sonlandırır (ve belki de oturumunuzu kapatır veya terminal penceresini kapatır!)

Örneğin

ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh% 

Buradayım ksh(normal kabuğum). Ben başlıyorum bashve sonra ben bash içinde exec /bin/echo. kshSonradan geri çekildiğimi görebiliyoruz çünkü bashsüreç yerini aldı /bin/echo.


yüzünde umm geri ksh b / c içine düştü süreci bu kadar mantıklı değil yankı ile değiştirildi?

14

TL; DR

execHerhangi bir komut belirtilmemişse, mevcut kabuk sürecini yenisiyle değiştirmek ve akış yeniden yönlendirme / dosya tanımlayıcılarını işlemek için kullanılır. evaldizeleri komut olarak değerlendirmek için kullanılır. Her ikisi de çalışma zamanında bilinen argümanlarla bir komut oluşturmak ve yürütmek için kullanılabilir, ancak execkomutları çalıştırmanın yanı sıra geçerli kabuğun işleminin yerini alır.

derleme

Sözdizimi:

exec [-cl] [-a name] [command [arguments]]

Kılavuza göre bu yerleşik komut belirtilmişse

... kabuğu değiştirir. Yeni bir işlem oluşturulmadı. Argümanlar komut verilecek argümanlar haline gelir.

Başka bir deyişle, bashPID 1234 ile çalışıyorsanız ve exec top -u rootbu kabuk içinde koşuyorsanız , topkomut PID 1234'e sahip olacak ve kabuk işleminizi değiştirecektir.

Bu nerede faydalı? Sarıcı komut dosyaları olarak bilinen bir şeyde. Bu tür komut dosyaları, argüman kümeleri oluşturur veya hangi değişkenlerin çevreye geçeceği konusunda kesin kararlar alır ve daha sonra execbelirtilen komutla kendini değiştirmek için kullanılır ve elbette sarmalayıcı komut dosyasının yol boyunca oluşturduğu aynı argümanları sağlar.

Kılavuzda ayrıca belirtilenler:

Komut belirtilmezse, mevcut kabukta herhangi bir yeniden yönlendirme etkili olur

Bu, mevcut kabuk çıktı akışlarından bir dosyayı bir dosyaya yönlendirmemize izin verir. Bu stdoutkomutları görmek istemediğiniz ancak yalnızca komutları görmek istediğinizde günlüğe kaydetme veya filtreleme amacıyla yararlı olabilir stderr. Örneğin, şöyle:

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

Bu davranış, kabuk komut dosyalarına giriş yapmak , akışları ayrı dosyalara veya işlemlere yönlendirmek ve dosya tanıtıcılarıyla diğer eğlenceli şeyleri işlemek için kullanışlıdır .

Kaynak kod düzeyinde en az bash4.3 sürüm için , execyerleşik olarak tanımlanır builtins/exec.def. Alınan komutları ayrıştırır ve eğer varsa, dosyada shell_execve()tanımlanmış olan fonksiyona geçer execute_cmd.c.

Uzun lafın kısası, execC programlama dilinde bir komut ailesi var ve shell_execve()temelde bir sarmalayıcı işlevi var execve:

/* Call execve (), handling interpreting shell scripts, and handling
   exec failures. */
int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{

eval yerleşik

Bash 4.3 el kitabı durumları (benim tarafımdan verilen vurgu):

Arglar tek bir komutta birlikte okunur ve birleştirilir. Bu komut daha sonra kabuk tarafından okunur ve yürütülür ve çıkış durumu eval değeri olarak döndürülür.

Oluşan işlem değişikliği olmadığını unutmayın. Aksine execamaç simüle etmek olduğu execve()işlevsellik, evalinşa sadece kullanıcı komut satırında bunları yazmamıştır sadece sanki argümanları "değerlendirmek" için hizmet vermektedir. Böylece, yeni süreçler yaratılır.

Bu nerede faydalı olabilir? Gilles , bu cevabın da belirttiği gibi "... eval çok sık kullanılmıyor. Bazı mermilerde, en yaygın kullanım, adı çalışma zamanına kadar bilinmeyen bir değişkenin değerini elde etmektir". Şahsen, kullanıcının şu anda kullanmakta olduğu belirli çalışma alanını temel alan bir komutu yürütmek / değerlendirmek için gerekli olan Ubuntu'daki birkaç senaryoda kullandım.

Kaynak kod seviyesinde, tanımlanır builtins/eval.defve ayrıştırılmış giriş dizesini evalstring()işlevine geçirir .

Diğer şeylerin yanı sıra, evalşu anki kabuk yürütme ortamında kalan değişkenleri de atayabilirexec :

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found

@ ctrl-alt-delor Bu bölümü düzenledim, teşekkürler, tartışmasız yeni bir süreç oluştursa da, PID sadece şu anki kabuğunkiyle aynı kalıyor. Gelecekte, özellikle 7 kelimelik cümle gibi küçük bir şey söz konusu olduğunda, yorum bırakmak ve oy kullanmak yerine, cevapları düzenlemeyi düşünün; Bir cevabı düzenlemek ve düzeltmek çok daha az zaman alır ve çok daha faydalıdır.
Sergiy Kolodyazhnyy

5
yeni bir alt süreç oluşturmak, argümanları çalıştırmak ve çıkış durumunu döndürmek.

Ah ne? Bütün mesele, evalhiçbir şekilde bir çocuk süreci yaratmamasıdır. Eğer yaparsam

eval "cd /tmp"

Bir kabukta, daha sonra mevcut kabuk dizini değiştirmiş olacak. execYeni bir alt süreç de yaratmaz, bunun yerine mevcut için geçerli yürütülebilir dosyayı (yani kabuk) değiştirir; işlem kimliği (ve açık dosyalar ve diğer şeyler) aynı kalır. Aksine eval, bir execsürece arama kabuk döndürmez execkendisi başarısız bağlı bulmak ya da yürütülebilir yük veya değişken genişletme sorunlarına kalıp yapamaz olmak.

evaltemelde argümanlarını birleştirme işleminden sonra bir dize olarak yorumlar, yani ekstra bir joker genişletme katmanı ve argüman bölme işlemi yapar. execböyle bir şey yapmaz.


1
Orijinal soru, "eval ve exec'in her ikisi de Bash (1) in yerleşik komutlarıdır ve komutları çalıştırır, yeni bir alt süreç oluşturur, argümanları çalıştırır ve çıkış durumunu döndürür"; Yanlış varsayımı değiştirdim.
Charles Stewart,

-1

Değerlendirme

Bu iş:

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi

Ancak, bunlar yok:

$ exec "echo hi"
bash: exec: echo hi: not found

$ "echo hi"
bash: echo hi: command not found

Görüntü değiştirme işlemi

Bu örnek exec, arama işlemindeki görüntünün nasıl değiştirildiğini gösterir :

# 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

exec echo $$Subshell PID ile koştu dikkat edin ! Ayrıca, tamamlandıktan sonra orijinal sh$kabuğumuza geri döndük .

Öte yandan, evalyok değil süreç görüntüsünü değiştirin. Aksine, normalde kabuğun içinde yaptığınız gibi verilen komutu çalıştırır. (Tabii ki, eğer bir sürecin doğmasını gerektiren bir emir çalıştırırsanız, sadece bunu yapar!)

sh$ echo $$
1234

sh$ sh

sh-subshell$ echo $$
5678

sh-subshell$ eval echo $$
5678

# We are still in the subshell!
sh-subshell$ echo $$
5678

Bu cevabı ben gönderdim, çünkü diğer örnekler bunu I gibi zayıf beyinler için yeterince anlaşılır bir şekilde açıklamadı.
Mateen Ulhaq

Zayıf sözcük seçimi: Süreç ikamei, burada örneklediğinizle ilgisiz görünen mevcut bir kavramdır .
muru

@muru "işlem değiştirme" daha mı iyi? Doğru terminolojinin ne olduğundan emin değilim. Manpage "süreç görüntüsünün değiştirilmesi" olarak adlandırılıyor.
Mateen Ulhaq,

Hmm, Dunno. (I "süreç görüntüsünü değiştirerek" duyduğunuzda Ama bence: exec)
muru
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.