Đây là những gì tôi cho là giải pháp tinh khiết-Bourne-shell tốt nhất để sử dụng như là cơ sở mà trên đó bạn có thể xây dựng "eet" của bạn:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
tôi nghĩ rằng đây là giải thích tốt nhất từ trong ra ngoài - command1 sẽ thực hiện và in ra thường xuyên trên stdout (file descriptor 1), sau đó khi nó đã được thực hiện, printf sẽ thực hiện và mã exit in command1 về stdout của nó , nhưng stdout đó được chuyển hướng đến bộ mô tả tập tin 3.
Khi lệnh1 đang chạy, giá trị của nó đang được chuyển sang lệnh 2 (đầu ra của printf không bao giờ làm cho lệnh 2) vì chúng tôi gửi nó đến bộ mô tả tập tin 3 thay vì 1, đó là những gì mà đường ống đọc). Sau đó, chúng ta chuyển hướng đầu ra của command2 thành tệp mô tả 4, để nó cũng nằm ngoài bộ mô tả tập tin 1 - bởi vì chúng ta muốn bộ mô tả tập tin miễn phí 1 chút sau, vì chúng ta sẽ đưa đầu ra printf trên bộ mô tả tập tin 3 trở lại vào bộ mô tả tập tin 1 - bởi vì đó là những gì thay thế lệnh (backticks), sẽ nắm bắt và đó là những gì sẽ được đặt vào biến.
Bit cuối cùng của phép thuật là exec 4>&1
đầu tiên chúng tôi đã thực hiện như một lệnh riêng biệt - nó sẽ mở tệp mô tả 4 làm bản sao của giá trị của vỏ bên ngoài. Lệnh thay thế sẽ nắm bắt bất cứ điều gì được viết trên tiêu chuẩn từ quan điểm của các lệnh bên trong nó - nhưng, vì đầu ra của command2 sẽ mô tả tập tin 4 đến mức thay thế lệnh có liên quan, thay thế lệnh không nắm bắt nó - tuy nhiên, một khi nó được "ra" của sự thay thế lệnh, nó có hiệu quả vẫn sẽ mô tả tập tin tổng thể của tập lệnh 1.
(exec 4>&1
phải là một lệnh riêng biệt vì nhiều shell thông thường không thích nó khi bạn cố gắng ghi vào một bộ mô tả tập tin bên trong một lệnh thay thế, được mở trong lệnh "bên ngoài" đang sử dụng sự thay thế. Vì vậy, đây là cách di động đơn giản nhất để làm điều đó.)
Bạn có thể xem nó bằng kỹ thuật ít hơn và nhiều hơn nữa playf Cách ul, như thể kết quả đầu ra của lệnh nhảy qua nhau: command1 pipe to command2, sau đó printf's output nhảy qua lệnh 2 để command2 không bắt được nó, và sau đó lệnh 2 xuất ra nhảy qua và ra lệnh thay thế cũng giống như printf vùng đất vừa kịp để bị bắt bởi sự thay thế để nó kết thúc trong biến, và đầu ra của command2 đi theo cách vui vẻ của nó được ghi vào đầu ra tiêu chuẩn, giống như trong một đường ống bình thường.
Ngoài ra, như tôi đã hiểu, $?
sẽ vẫn chứa mã trả về của lệnh thứ hai trong đường ống, vì các phép gán biến, lệnh thay thế và lệnh ghép đều có hiệu quả trong suốt đối với mã trả về của lệnh bên trong chúng, do đó trạng thái trả về của command2 sẽ được truyền đi. Một báo trước là có thể lệnh1 sẽ kết thúc bằng cách sử dụng các bộ mô tả tập tin 3 hoặc 4, hoặc lệnh 2 hoặc bất kỳ lệnh nào sau này sẽ sử dụng bộ mô tả tập tin 4, để mạnh mẽ hơn, bạn sẽ làm:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Lưu ý rằng tôi sử dụng lệnh hợp chất trong ví dụ của tôi, nhưng subshells (sử dụng ()
thay vì { }
cũng sẽ làm việc, mặc dù có thể có lẽ là ít hiệu quả.)
Commands kế thừa mô tả tập tin từ quá trình này mà ra mắt chúng, vì vậy toàn bộ dòng thứ hai sẽ kế thừa tệp mô tả bốn và lệnh ghép nối theo sau là 3>&1
sẽ kế thừa bộ mô tả tập tin ba.Vì vậy, các 4>&-
đảm bảo rằng các hợp chất bên trong lệnh sẽ không kế thừa tập tin mô tả bốn, và 3>&-
sẽ không kế thừa tập tin mô tả ba, vì vậy command1 được một 'sạch hơn', môi trường tiêu chuẩn hơn. Bạn cũng có thể di chuyển bên trong 4>&-
bên cạnh 3>&-
, nhưng tôi hình tại sao không chỉ giới hạn phạm vi của nó càng nhiều càng tốt.
Tôi không chắc tần suất mọi thứ sử dụng bộ mô tả tập tin trực tiếp - tôi nghĩ hầu hết các chương trình thời gian sử dụng syscalls trả về mô tả tệp không được sử dụng tại thời điểm, nhưng đôi khi mã ghi vào bộ mô tả tệp 3 trực tiếp, tôi đoán (tôi có thể tưởng tượng một chương trình kiểm tra một mô tả tập tin để xem nếu nó mở, và sử dụng nó nếu nó được, hoặc hành xử khác nhau cho phù hợp nếu nó không). Vì vậy, sau này có lẽ là tốt nhất để ghi nhớ và sử dụng cho các trường hợp có mục đích chung.
--- nội dung lỗi thời dưới dòng này ---
Vì lý do lịch sử, đây là bản gốc, tôi không-di-to-all-vỏ trả lời:
[EDIT] xấu của tôi, điều này không làm việc với bash vì bash cần thêm coddling khi không quan trọng với các bộ mô tả tập tin, tôi sẽ cập nhật điều này ngay khi có thể. [/ EDIT]
tinh khiết Bourne giải pháp vỏ:
exitstatus=`{ 3>&- command1; } 1>&3; printf $?` 3>&1 | command2
# $exitstatus now has command1's exit status.
Đây là cơ sở mà trên đó bạn có thể xây dựng "eet" của bạn. Slap trong một số phân tích lập luận commandline và tất cả những gì, rẽ command2 vào "tee" với các tùy chọn liên quan vv
Lời giải thích rất chi tiết như sau:
Ở cấp độ đầu, báo cáo kết quả chỉ là một ống giữa hai lệnh:
commandA | command2
commandA lần lượt bị phá vỡ một lệnh duy nhất với một chuyển hướng của tập tin mô tả từ 3 đến mô tả tập tin 1 (stdout):
commandB 3>&1
Điều này có nghĩa là trình bao sẽ mong đợi lệnh B để viết một cái gì đó vào bộ mô tả tập tin 3 - nếu bộ mô tả tập tin 3 không bao giờ được mở, nó sẽ là một lỗi. Nó cũng có nghĩa là command2 sẽ nhận được bất cứ điều gì commandB đầu ra trên cả hai tập tin mô tả 1 (stdout) và 3.
commandB lần lượt là một nhiệm vụ biến sử dụng thay thế lệnh:
VAR_FOO=`commandC`
Chúng ta biết rằng các bài tập biến không in bất cứ thứ gì trên bất kỳ bộ mô tả tệp nào (và lệnh stdout của commandC được bắt giữ để thay thế), vì vậy chúng ta biết rằng toàn bộ lệnh sẽ không xuất ra bất cứ thứ gì trên stdout. command2 do đó sẽ chỉ nhìn thấy những gì commandC viết để mô tả tập tin 3.
Và commandC là hai lệnh, nơi mà các lệnh in thứ hai trạng thái thoát những người đầu tiên:
commandD ; printf $?
Vì vậy, bây giờ chúng ta đã biết việc giao biến trong bước cuối cùng sẽ chứa trạng thái thoát của lệnhD.
Bây giờ, commandD phân hủy khác chuyển hướng cơ bản, các thiết bị xuất chuẩn của comman nộp mô tả 3:
commandE 1>&3
Vì vậy, bây giờ chúng ta biết rằng việc viết điều cần mô tả tập tin 3, và do đó cuối cùng để Command2, là commande của stdout.
Cuối cùng: commande là một "lệnh phức hợp" (bạn cũng có thể sử dụng một subshell ở đây, nhưng nó không phải là hiệu quả), gói xung quanh một loại ít thường thấy của "chuyển hướng":
{ 3>&- command1; }
(Đó 3>&-
là một chút khó khăn vì vậy chúng tôi sẽ trở lại với nó ở cuối.) Vì vậy, các lệnh phức hợp làm cho dấu chấm phẩy bắt buộc khi lệnh cuối cùng và cú đúp cuối cùng là trên cùng một dòng, đó là lý do tại sao đó là có. Vì vậy, chúng ta biết các lệnh ghép nối trả về mã thoát của lệnh cuối cùng của chúng và chúng kế thừa các bộ mô tả tệp như mọi thứ khác, vì vậy chúng ta biết rằng lệnh stdout của command1 thoát ra khỏi lệnh ghép, chuyển hướng đến bộ mô tả tập tin 3 để tránh bị bắt bởi lệnh thay thế , trong khi đó việc thay thế lệnh sẽ bắt giữ stdout còn lại của printf, điều này cho thấy trạng thái thoát của lệnh1 khi nó được thực hiện.
Và bây giờ cho bit phức tạp: 3>&-
nói "đóng bộ mô tả tập tin 3". Bạn có thể nghĩ, "tại sao bạn đóng nó khi bạn chỉ chuyển hướng đầu ra của lệnh1 đến nó?" Vâng, nếu bạn nhìn kỹ, bạn sẽ thấy rằng các hiệu ứng đóng chỉ có lệnh1 bên trong lệnh ghép (bên trong dấu ngoặc nhọn) đặc biệt, trong khi chuyển hướng tác động đến toàn bộ lệnh ghép. Vì vậy, đây là những gì sẽ xảy ra: do thời gian các lệnh riêng lẻ của lệnh ghép chạy, trình bao mở mô tả tập tin 3. Quy trình kế thừa các bộ mô tả tập tin, do đó, lệnh1, theo mặc định sẽ chạy với bộ mô tả tập tin 3 mở và trỏ đến cùng một nơi. Điều này là xấu bởi vì đôi khi, các chương trình thực sự mong đợi các mô tả tập tin cụ thể có nghĩa là những điều đặc biệt - chúng có thể hoạt động khác nhau khi được khởi chạy với bộ mô tả tập tin 3 mở. Giải pháp mạnh mẽ nhất là chỉ đóng bộ mô tả tập tin 3 (hoặc bất kể số nào bạn sử dụng) chỉ với lệnh1, vì vậy nó chạy như thể nếu bộ mô tả tập tin 3 chưa bao giờ được mở.
Tôi giả định rằng câu hỏi thực sự ở đây là: làm thế nào để phát ra đầu ra và nắm bắt trạng thái thoát. Nếu vậy: có thể trùng lặp với [bash: tee output AND exit exit status] (http://stackoverflow.com/questions/1221833/bash-tee-output-and-capture-exit-status) – lesmana