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 echo
và this 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à 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ả.
Tôi đã học được điều gì đó! – blueshift
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 –
@ J-16SDiZ được khắc phục như bạn đã đề xuất, nhưng vấn đề vẫn tồn tại – artaxerxe