2012-03-29 16 views
36

Từ Bash Reference Manual tôi nhận được khoảng exec bash BUILTIN lệnh sau:Cần giải thích cho Linux bash BUILTIN exec lệnh hành vi

Nếu lệnh được cung cấp, nó sẽ thay thế vỏ mà không cần tạo một quá trình mới.

Bây giờ tôi có những điều sau bash kịch bản:

#!/bin/bash 
exec ls; 
echo 123; 
exit 0 

này được thực hiện, tôi nhận điều này:

cleanup.sh ex1.bash file.bash file.bash~ output.log 
(files from the current directory) 

Bây giờ, nếu tôi có kịch bản này:

#!/bin/bash 
exec ls | cat 
echo 123 
exit 0 

Tôi nhận được kết quả sau:

cleanup.sh 
ex1.bash 
file.bash 
file.bash~ 
output.log 
123 

Câu hỏi của tôi là:

Nếu khi exec được gọi nó thay thế vỏ mà không cần tạo một quá trình mới, tại sao khi đưa | cat, các echo 123 được in, nhưng không có nó, nó không phải là. Vì vậy, tôi sẽ rất vui nếu ai đó có thể giải thích logic của hành vi này là gì.

Cảm ơn.

EDIT: Sau khi phản ứng @torek, tôi nhận được một thậm chí khó khăn hơn để giải thích hành vi:

1. exec ls>out lệnh tạo ra các tập tin out và đặt trong đó kết quả lệnh 's ls;

2. exec ls>out1 ls>out2 chỉ tạo các tệp nhưng không đưa vào bên trong bất kỳ kết quả nào. Nếu lệnh hoạt động như được đề xuất, tôi nghĩ rằng lệnh số 2 sẽ có kết quả tương tự như lệnh số 1 (thậm chí nhiều hơn, tôi nghĩ nó không nên tạo tệp out2).

+4

Tôi đã học được điều gì đó! – blueshift

+1

Bạn liên kết trên 'exec' là hàm' 'exec'. Những gì bạn đang thử nghiệm là 'bash'' exec' buildin. Chúng không giống nhau. Xem www.gnu.org/software/bash/manual/bashref.html –

+0

@ J-16SDiZ được khắc phục như bạn đã đề xuất, nhưng vấn đề vẫn tồn tại – artaxerxe

Trả lời

38

Trong trường hợp cụ thể này, bạn có exec trong một đường ống. Để thực hiện một loạt các lệnh đường ống, vỏ phải ban đầu là ngã ba, tạo một lớp con. (Cụ thể nó phải tạo ra đường ống, sau đó ngã ba, để mọi thứ chạy "bên trái" của đường ống có thể có đầu ra của nó được gửi đến bất cứ cái gì là "bên phải" của đường ống.)

Để thấy điều này là trong thực tế những gì đang xảy ra, so sánh:

{ ls; echo this too; } | cat 

với:

{ exec ls; echo this too; } | cat 

các chạy cựu ls mà không rời khỏi tiểu vỏ, do đó tiểu vỏ do đó, vẫn còn xung quanh để chạy echo . Sau đó chạy ls bằng cách thoát khỏi vỏ phụ, do đó không còn ở đó để thực hiện echothis too không được in.

(Việc sử dụng xoăn-niềng răng { cmd1; cmd2; } thường ngăn chặn các ngã ba hành động tiểu vỏ mà bạn nhận được với ngoặc (cmd1; cmd2), nhưng trong trường hợp của một đường ống, ngã ba là "buộc", vì nó là.)

Chuyển hướng vỏ hiện tại chỉ xảy ra nếu có "không có gì để chạy", giống như sau từ exec. Do đó, ví dụ: exec >stdout 4<input 5>>append sửa đổi vỏ hiện tại, nhưng exec foo >stdout 4<input 5>>append cố gắng thực thi lệnh foo. [Lưu ý: đây không phải là chính xác; xem thêm phụ lục.]

Điều thú vị là, trong một vỏ tương tác, sau exec foo >output không thành công vì không có lệnh foo, vỏ gậy xung quanh, nhưng giá trị mặc định vẫn được chuyển hướng đến tệp output. (Bạn có thể phục hồi với exec >/dev/tty Trong một kịch bản, sự thất bại để exec foo chấm dứt kịch bản..)


Với một đỉnh của mũ để @ Pumbaa80, đây là một cái gì đó thậm chí minh họa hơn:

#! /bin/bash 
shopt -s execfail 
exec ls | cat -E 
echo this goes to stdout 
echo this goes to stderr 1>&2 

(lưu ý: cat -E được đơn giản hóa từ thông thường của tôi cat -vET, đó là tiện dụng của tôi go-to cho "hãy để tôi xem các ký tự không in theo một cách dễ nhận biết"). Khi tập lệnh này chạy, đầu ra từ ls đã áp dụng cat -E (trên Linux điều này làm cho dòng cuối hiển thị dưới dạng ký hiệu $), nhưng đầu ra được gửi tới stdout và stderr (trên hai dòng còn lại) là không phải được chuyển hướng . Thay đổi | cat -E thành > out và sau khi tập lệnh chạy, hãy quan sát nội dung của tệp out: hai số cuối cùng là echo không có trong đó.

Bây giờ, hãy thay đổi ls thành foo (hoặc một số lệnh khác sẽ không được tìm thấy) và chạy lại tập lệnh. Lần này đầu ra là:

$ ./demo.sh 
./demo.sh: line 3: exec: foo: not found 
this goes to stderr 

và file out hiện nay có các nội dung được tạo ra bởi dòng đầu tiên echo.

Điều này làm cho những gì exec "thực sự" rõ ràng nhất có thể (nhưng không rõ ràng hơn, như Albert Einstein đã không đặt nó :-)).

Thông thường, khi trình đơn thực thi "lệnh đơn giản" (xem trang hướng dẫn để xác định chính xác, nhưng điều này loại trừ các lệnh trong "đường ống"), nó chuẩn bị mọi hoạt động chuyển hướng I/O được chỉ định <, >, v.v. bằng cách mở các tệp cần thiết. Sau đó, vỏ gọi fork (hoặc một số biến thể tương đương nhưng hiệu quả hơn như vfork hoặc clone tùy thuộc vào hệ điều hành cơ sở, cấu hình, v.v ...). mong muốn thỏa thuận cuối cùng: > out di chuyển các mô tả mở cửa cho fd 1-stdout-while 6> out di chuyển các mô tả mở cửa cho fd 6.

Nếu bạn chỉ định exec từ khóa, tuy nhiên, vỏ ngăn chặn bước fork. Nó làm tất cả các tập tin mở và tập tin mô tả-sắp xếp lại như bình thường, nhưng thời gian này, nó ảnh hưởng đến bất kỳ và tất cả các lệnh sau. Cuối cùng, khi đã thực hiện tất cả các chuyển hướng, vỏ sẽ cố gắng để execve() (theo nghĩa hệ thống) lệnh, nếu có.Nếu không có lệnh, hoặc nếu cuộc gọi execve() không thành công vỏ được cho là sẽ tiếp tục chạy (tương tác hoặc bạn đã đặt execfail), các binh sĩ vỏ trên đó. Nếu execve() thành công, trình bao không còn tồn tại, đã được thay thế bằng lệnh mới. Nếu execfail không được đặt và vỏ không tương tác, trình bao sẽ thoát.

(Ngoài ra còn có các biến chứng thêm của hàm command_not_found_handle shell: bash của exec dường như để ngăn chặn chạy nó, dựa trên kết quả kiểm tra từ khóa exec nói chung làm cho vỏ không nhìn vào chức năng riêng của mình, ví dụ, nếu bạn có một. chức năng vỏ f, chạy f như một lệnh đơn giản chạy chức năng vỏ, cũng như (f) chạy nó trong một vỏ phụ, nhưng chạy (exec f) bỏ qua nó.)


Vì lý do tại sao ls>out1 ls>out2 tạo hai tệp (có hoặc không có exec), điều này là đơn giản, đủ: vỏ mở mỗi chuyển hướng, và sau đó sử dụng dup2 để di chuyển các mô tả tập tin. Nếu bạn có hai chuyển hướng > thông thường, shell sẽ mở cả hai, di chuyển cái đầu tiên sang fd 1 (stdout), sau đó di chuyển cái thứ hai sang fd 1 (stdout một lần nữa), đóng cái đầu tiên trong tiến trình. Cuối cùng, nó chạy ls ls, bởi vì đó là những gì còn lại sau khi xóa >out1 >out2. Miễn là không có tệp nào có tên là ls, lệnh ls than phiền với stderr và không viết gì cả.

+1

Đối với vỏ tương tác (không), manpage nói "Nếu lệnh' không thể được thực hiện vì một lý do nào đó, một shell không tương tác thoát, trừ khi tùy chọn shell 'execfail' được kích hoạt, trong trường hợp này nó trả về lỗi. Một shell tương tác trả về lỗi" – user123444555621

+0

@torek nhìn vào phần EDIT trong câu hỏi – artaxerxe

+0

@ Pumbaa80 : rất đẹp; điều này giúp làm cho "cách hoạt động của exec" rõ ràng hơn. Tôi sẽ thêm một ghi chú vào câu trả lời của tôi. – torek