Sự khác biệt giữa eval và exec là gì?


81

evalexeccả hai đều được xây dựng trong các lệnh của bash (1) để thực thi các lệnh.

Tôi cũng thấy execcó một vài lựa chọn nhưng đó có phải là sự khác biệt duy nhất? Điều gì xảy ra với bối cảnh của họ?



Câu trả lời:


124

evalexeclà những con thú hoàn toàn khác nhau. (Ngoài thực tế là cả hai sẽ chạy các lệnh, nhưng mọi thứ bạn làm trong một vỏ cũng vậy.)

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

Những gì exec cmdkhông, hoàn toàn giống như chỉ chạy cmd, ngoại trừ lớp vỏ hiện tại được thay thế bằng lệnh, thay vì một quá trình riêng biệt đang được chạy. Trong nội bộ, chạy nói /bin/lssẽ gọi fork()để tạo ra một quy trình con, và sau đó exec()trong đứa trẻ để thực thi /bin/ls. exec /bin/lsmặt khác sẽ không ngã ba, mà chỉ thay thế vỏ.

So sánh:

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

với

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

echo $$in ra PID của shell mà tôi đã khởi động và việc liệt kê /proc/selfcung cấp cho chúng tôi PID của lsshell được chạy từ shell. Thông thường, ID tiến trình là khác nhau, nhưng có execvỏ và lscó cùng ID tiến trình. Ngoài ra, lệnh sau execkhông chạy, vì shell đã được thay thế.


Mặt khác:

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

evalsẽ chạy các đối số như một lệnh trong shell hiện tại. Nói cách khác eval foo barlà giống như chỉ foo bar. Nhưng các biến sẽ được mở rộng trước khi thực hiện, vì vậy chúng ta có thể thực thi các lệnh được lưu trong các biến shell:

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

Nó sẽ không tạo ra một tiến trình con, vì vậy biến được đặt trong trình bao hiện tại. (Tất nhiên eval /bin/lssẽ tạo ra một quy trình con, giống như cách mà một người già đơn giản /bin/lssẽ làm.)

Hoặc chúng ta có thể có một lệnh xuất ra các lệnh shell. Chạy ssh-agentbắt đầu tác nhân trong nền và xuất ra một loạt các phép gán biến, có thể được đặt trong trình bao hiện tại và được sử dụng bởi các tiến trình con (các sshlệnh bạn sẽ chạy). Do đó ssh-agentcó thể được bắt đầu với:

eval $(ssh-agent)

Và shell hiện tại sẽ lấy các biến cho các lệnh khác để kế thừa.


Tất nhiên, nếu biến số cmdcó chứa thứ gì đó như thế rm -rf $HOME, thì chạy eval "$cmd"sẽ không phải là thứ bạn muốn làm. Ngay cả những thứ như thay thế lệnh bên trong chuỗi sẽ được xử lý, vì vậy người ta phải thực sự chắc chắn rằng đầu vào evallà an toàn trước khi sử dụng nó.

Thông thường, có thể tránh evalvà tránh thậm chí vô tình trộn mã và dữ liệu sai cách.


Đó là một câu trả lời tuyệt vời, @ilkkachu. Cảm ơn!
Willian Paixao

Thông tin chi tiết về việc sử dụng eval có thể được tìm thấy ở đây: stackoverflow.com/a/46944004/2079103
Clearlight

1
@clearlight, tốt, điều đó nhắc nhở tôi thêm từ chối trách nhiệm thông thường về việc không sử dụng evalở nơi đầu tiên cho câu trả lời này quá. Những thứ như biến đổi gián tiếp có thể được thực hiện trong nhiều shell thông qua declare/ typeset/ namerefvà mở rộng như thế ${!var}, vì vậy tôi sẽ sử dụng chúng thay vì evaltrừ khi tôi thực sự phải tránh nó.
ilkkachu

27

execkhông tạo ra một quy trình mới. Nó thay thế quy trình hiện tại bằng lệnh mới. Nếu bạn đã làm điều này trên dòng lệnh thì nó sẽ kết thúc phiên shell của bạn một cách hiệu quả (và có thể đăng xuất bạn hoặc đóng cửa sổ terminal!)

ví dụ

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

Tôi đang ở đây ksh(vỏ bình thường của tôi). Tôi bắt đầu bashvà sau đó bên trong bash tôi exec /bin/echo. Chúng ta có thể thấy rằng tôi đã bị rơi trở lại kshsau đó vì bashquá trình này được thay thế bằng /bin/echo.


umm trên mặt rơi trở lại vào quá trình ksh b / c đã được thay thế bởi tiếng vang không có ý nghĩa nhiều?

14

TL; DR

execđược sử dụng để thay thế quy trình shell hiện tại bằng bộ mô tả chuyển hướng / tệp mới và xử lý luồng nếu không có lệnh nào được chỉ định. evalđược sử dụng để đánh giá các chuỗi như các lệnh. Cả hai có thể được sử dụng để xây dựng và thực thi một lệnh với các đối số được biết đến trong thời gian chạy, nhưng execthay thế quá trình của trình bao hiện tại bên cạnh việc thực thi các lệnh.

thực hiện buil-in

Cú pháp:

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

Theo hướng dẫn nếu có lệnh được chỉ định tích hợp sẵn này

... Thay thế vỏ. Không có quá trình mới được tạo ra. Các đối số trở thành đối số để chỉ huy.

Nói cách khác, nếu bạn đang chạy bashvới PID 1234 và nếu bạn chạy exec top -u roottrong lớp vỏ đó, thì toplệnh đó sẽ có PID 1234 và thay thế quy trình shell của bạn.

Cái này hữu ích ở đâu? Trong một cái gì đó được gọi là kịch bản lệnh bao bọc. Các tập lệnh như vậy xây dựng các tập hợp đối số hoặc đưa ra quyết định nhất định về biến nào sẽ truyền vào môi trường và sau đó sử dụng execđể thay thế chính nó bằng bất kỳ lệnh nào được chỉ định và tất nhiên cung cấp các đối số tương tự mà tập lệnh bao bọc đã tạo ra trên đường đi.

Những gì hướng dẫn cũng nêu là:

Nếu lệnh không được chỉ định, mọi chuyển hướng đều có hiệu lực trong trình bao hiện tại

Điều này cho phép chúng tôi chuyển hướng bất cứ thứ gì từ luồng đầu ra của shell hiện tại vào một tệp. Điều này có thể hữu ích cho mục đích ghi nhật ký hoặc lọc, nơi bạn không muốn thấy stdoutcác lệnh mà chỉ stderr. Chẳng hạn, như vậy:

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

Hành vi này giúp thuận tiện cho việc đăng nhập các tập lệnh shell , chuyển hướng các luồng đến các tệp hoặc quy trình riêng biệt và các nội dung thú vị khác với các mô tả tệp.

Trên cấp mã nguồn ít nhất là cho bashphiên bản 4.3, tích exechợp được xác định trong builtins/exec.def. Nó phân tích cú pháp các lệnh đã nhận và nếu có bất kỳ lệnh nào, nó sẽ chuyển mọi thứ vào shell_execve()hàm được định nghĩa trong execute_cmd.ctệp.

Tóm lại, tồn tại một nhóm execlệnh trong ngôn ngữ lập trình C và shell_execve()về cơ bản là một hàm bao của execve:

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

tích hợp sẵn

Các trạng thái thủ công bash 4.3 (nhấn mạnh thêm bởi tôi):

Các đối số được đọc và nối với nhau thành một lệnh duy nhất. Lệnh này sau đó được đọc và thực thi bởi shell và trạng thái thoát của nó được trả về là giá trị của eval.

Lưu ý rằng không có quá trình thay thế xảy ra. Không giống như execmục tiêu là mô phỏng execve()chức năng, tích evalhợp chỉ phục vụ để "đánh giá" các đối số, giống như khi người dùng đã gõ chúng trên dòng lệnh. Như vậy, các quy trình mới được tạo ra.

Trường hợp này có thể hữu ích? Như Gilles đã chỉ ra trong câu trả lời này , "... eval không được sử dụng thường xuyên. Trong một số shell, cách sử dụng phổ biến nhất là lấy giá trị của một biến không biết tên cho đến khi chạy". Cá nhân, tôi đã sử dụng nó trong một vài tập lệnh trên Ubuntu khi cần thực thi / đánh giá một lệnh dựa trên không gian làm việc cụ thể mà người dùng hiện đang sử dụng.

Ở cấp độ mã nguồn, nó được xác định trong builtins/eval.defvà chuyển chuỗi đầu vào được phân tích cú pháp sang evalstring()hàm.

Trong số những thứ khác, evalcó thể gán các biến vẫn còn trong môi trường thực thi shell hiện tại, trong khi execkhông thể:

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

@ ctrl-alt-delor Tôi đã chỉnh sửa phần đó, cảm ơn, mặc dù có thể cho rằng nó sinh ra một quy trình mới, nhưng PID chỉ đơn thuần giống như trình bao hiện tại. Trong tương lai hãy xem xét chỉnh sửa câu trả lời, thay vì để lại một bình luận và downvote, đặc biệt là khi câu hỏi nhỏ như cụm từ 7 từ đang được đề cập; nó ít tốn thời gian hơn để chỉnh sửa và sửa câu trả lời, và nó hữu ích hơn nhiều.
Sergiy Kolodyazhnyy

5
tạo một tiến trình con mới, chạy các đối số và trả về trạng thái thoát.

Uh cái gì Toàn bộ vấn đề evallà nó không theo bất kỳ cách nào tạo ra một quy trình con. Nếu tôi làm

eval "cd /tmp"

trong một shell, sau đó shell hiện tại sẽ có thư mục thay đổi. Không exectạo ra một tiến trình con mới, thay vào đó, nó thay đổi tệp thực thi hiện tại (cụ thể là shell) cho cái đã cho; id quá trình (và các tệp đang mở và các thứ khác) giữ nguyên. Trái ngược với eval, một execsẽ không trở lại vỏ gọi trừ khi execchính nó bị lỗi do không thể tìm hoặc tải tệp thực thi hoặc chết để tranh luận về các vấn đề mở rộng.

evalvề cơ bản diễn giải (các) đối số của nó dưới dạng một chuỗi sau khi ghép, cụ thể là nó sẽ thực hiện thêm một lớp mở rộng ký tự đại diện và phân tách đối số. execkhông làm bất cứ điều gì như thế.


1
Câu hỏi ban đầu đọc "eval và exec là cả hai lệnh dựng sẵn của Bash (1) và thực thi các lệnh, tạo một tiến trình con mới, chạy các đối số và trả về trạng thái thoát"; Tôi đã chỉnh sửa giả định sai.
Charles Stewart

-1

Đánh giá

Những công việc này:

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi

Tuy nhiên, những điều này không:

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

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

Quá trình thay thế hình ảnh

Ví dụ này cho thấy cách execthay thế hình ảnh của quá trình gọi của nó:

# 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

Lưu ý rằng exec echo $$chạy với PID của subshell! Hơn nữa, sau khi nó hoàn thành, chúng tôi đã trở lại trong sh$vỏ ban đầu của chúng tôi .

Mặt khác, evalkhông phải thay thế hình ảnh quá trình. Thay vào đó, nó chạy lệnh đã cho như bình thường trong vỏ. (Tất nhiên, nếu bạn chạy một lệnh yêu cầu một quá trình được sinh ra ... nó sẽ làm điều đó!)

sh$ echo $$
1234

sh$ sh

sh-subshell$ echo $$
5678

sh-subshell$ eval echo $$
5678

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

Tôi đã đăng câu trả lời này vì các ví dụ khác không thực sự minh họa điều này một cách đủ dễ hiểu cho những bộ óc yếu đuối như I.
Mateen Ulhaq

Lựa chọn từ ngữ kém: Thay thế quá trình là một khái niệm hiện có dường như không liên quan đến bất cứ điều gì bạn đang minh họa ở đây.
muru

@muru "thay thế quá trình" tốt hơn? Tôi không chắc thuật ngữ chính xác là gì. Trang chủ dường như gọi nó là "thay thế hình ảnh quá trình".
Mateen Ulhaq

Hừm, dunno. (Nhưng khi tôi nghe "thay thế hình ảnh quá trình", tôi nghĩ exec:)
muru
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.