eval
ve exec
her ikisi de komutları çalıştıran bash (1) komutlarında yerleşiktir.
Ayrıca exec
birkaç seçeneğim var ama tek fark bu mu? Bağlamlarına ne olur?
eval
ve exec
her ikisi de komutları çalıştıran bash (1) komutlarında yerleşiktir.
Ayrıca exec
birkaç seçeneğim var ama tek fark bu mu? Bağlamlarına ne olur?
Yanıtlar:
eval
ve exec
tamamen 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 cmd
ki, 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/self
bize ls
kabuğun koştuğu PID'yi veriyor . Genellikle işlem kimlikleri farklıdır, ancak exec
kabukla ls
aynı işlem kimliğine sahiptir. Ayrıca, exec
kabuk 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.
eval
argü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/ls
bir çocuk süreci yaratacak, aynı düz bir yaşlı /bin/ls
olacağı 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 eval
kullanmadan önce güvenlidir.
Genellikle, eval
kod 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 .
eval
konusundaki olağan feragatnameyi de bu cevaba eklemeyi hatırlattı . Dolaylı olarak değiştirici değişkenler gibi şeyler , declare
/ typeset
/ nameref
ve benzeri gibi birçok kabukta yapılabilir ${!var}
, bu yüzden onlardan eval
kaçınmak zorunda olmadıkça, yerine bunları kullanırdım .
exec
yeni 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 bash
ve sonra ben bash içinde exec /bin/echo
. ksh
Sonradan geri çekildiğimi görebiliyoruz çünkü bash
süreç yerini aldı /bin/echo
.
exec
Herhangi 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. eval
dizeleri 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 exec
komutları çalıştırmanın yanı sıra geçerli kabuğun işleminin yerini alır.
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, bash
PID 1234 ile çalışıyorsanız ve exec top -u root
bu kabuk içinde koşuyorsanız , top
komut 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 exec
belirtilen 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 stdout
komutları 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 bash
4.3 sürüm için , exec
yerleş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ı, exec
C 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;
{
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 exec
amaç simüle etmek olduğu execve()
işlevsellik, eval
inş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.def
ve 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
yeni bir alt süreç oluşturmak, argümanları çalıştırmak ve çıkış durumunu döndürmek.
Ah ne? Bütün mesele, eval
hiç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. exec
Yeni 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 exec
sürece arama kabuk döndürmez exec
kendisi 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.
eval
temelde 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. exec
böyle bir şey yapmaz.
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, eval
yok 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
exec
)