Vì vậy, tôi muốn đóng góp một câu trả lời như của lesmana, nhưng tôi nghĩ rằng giải pháp của tôi có lẽ đơn giản hơn một chút và có lợi hơn một chút:
# 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 điều này được giải thích tốt nhất từ trong ra ngoài - lệnh1 sẽ thực thi và in đầu ra thông thường của nó trên thiết bị xuất chuẩn (mô tả tệp 1), sau đó khi hoàn tất, printf sẽ thực thi và in mã thoát của icommand1 trên thiết bị xuất chuẩn của nó, nhưng thiết bị xuất chuẩn đó được chuyển hướng đến mô tả tập tin 3.
Trong khi lệnh1 đang chạy, thiết bị xuất chuẩn của nó đang được chuyển sang lệnh2 (đầu ra của printf không bao giờ chuyển sang lệnh2 vì chúng tôi gửi nó tới tệp mô tả 3 thay vì 1, đó là những gì đường ống đọc). Sau đó, chúng tôi chuyển hướng đầu ra của lệnh2 sang bộ mô tả tệp 4, do đó nó cũng nằm ngoài bộ mô tả tệp 1 - vì chúng tôi muốn mô tả tệp 1 miễn phí sau một chút, vì chúng tôi sẽ đưa đầu ra printf trên bộ mô tả tệp 3 trở lại vào bộ mô tả tệp 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.
Điều kỳ diệu cuối cùng là lần đầu tiên exec 4>&1
chúng tôi thực hiện như một lệnh riêng - nó mở mô tả tệp 4 dưới dạng bản sao của thiết bị xuất chuẩn của lớp vỏ ngoài. Thay thế lệnh sẽ nắm bắt bất cứ điều gì được viết trên tiêu chuẩn từ phối cảnh của các lệnh bên trong nó - nhưng vì đầu ra của lệnh2 sẽ chuyển sang mô tả 4 khi có liên quan đến việc thay thế lệnh, nên thay thế lệnh sẽ không nắm bắt được - tuy nhiên một khi nó được "ra" thay thế lệnh, nó vẫn thực sự đi đến phần 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 vì nhiều shell thông thường không thích nó khi bạn cố ghi vào một mô tả tệp bên trong thay thế lệnh, được mở trong lệnh "bên ngoài" đang sử dụng 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 xét nó theo cách ít kỹ thuật và vui tươi hơn, vì nếu các đầu ra của các lệnh đang nhảy vọt vào nhau: Command1 pipe sang lệnh2, thì đầu ra của printf nhảy qua lệnh 2 để lệnh2 không bắt được nó, và sau đó Đầu ra của lệnh 2 nhảy qua và thay thế lệnh giống như printf 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 lệnh2 tiếp tục đượ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, theo tôi hiểu, nó $?
vẫn sẽ chứa mã trả về của lệnh thứ hai trong đường ống, bởi vì các phép gán biến, thay thế lệnh và các lệnh ghép đều hoàn toàn 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 nên được truyền bá - điều này, và không phải xác định một chức năng bổ sung, là lý do tại sao tôi nghĩ rằng đây có thể là một giải pháp tốt hơn so với giải pháp được đề xuất bởi lesmana.
Theo đề xuất của lesmana, có thể lệnh1 sẽ kết thúc bằng cách sử dụng mô tả tệp 3 hoặc 4, vì vậy để 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 các lệnh ghép trong ví dụ của mình, nhưng các lớp con (sử dụng ( )
thay vì { }
cũng sẽ hoạt động, mặc dù có lẽ sẽ kém hiệu quả hơn.)
Các lệnh kế thừa bộ mô tả tệp từ quá trình khởi chạy chúng, vì vậy toàn bộ dòng thứ hai sẽ kế thừa bộ mô tả tệp bốn và lệnh ghép tiếp theo 3>&1
sẽ kế thừa bộ mô tả tệp ba. Vì vậy, 4>&-
đảm bảo rằng lệnh ghép bên trong sẽ không kế thừa bộ mô tả tệp bốn và 3>&-
sẽ không kế thừa bộ mô tả tệp ba, vì vậy lệnh1 có được 'môi trường sạch hơn', chuẩn hơn. Bạn cũng có thể di chuyển bên trong bên 4>&-
cạnh 3>&-
, nhưng tôi hiểu 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 chắn tần suất mọi thứ sử dụng bộ mô tả tệp ba và bốn trực tiếp - Tôi nghĩ rằng hầu hết các chương trình sử dụng các tòa nhà chọc trời trả lại các mô tả tệp không được sử dụng tại thời điểm đó, nhưng đôi khi mã được ghi trực tiếp vào bộ mô tả tệp 3, tôi đoán (tôi có thể tưởng tượng một chương trình kiểm tra một bộ mô tả tệp để xem nó có mở không và sử dụng nó nếu có, hoặc hành xử khác đi nếu không). Vì vậy, cái sau có lẽ là tốt nhất để ghi nhớ và sử dụng cho các trường hợp mục đích chung.