2012-01-23 33 views
22

Tôi đang sử dụng một đường ống của một số lệnh trong bash. Có cách nào để cấu hình bash để chấm dứt tất cả các lệnh trong toàn bộ đường ống ngay lập tức nên một trong các lệnh thất bại?Vỏ ống: Thoát ngay lập tức khi một lệnh không thành công

Trong trường hợp của tôi, lệnh đầu tiên, nói command1, chạy một lúc cho đến khi nó tạo ra một số đầu ra. Ví dụ: bạn có thể thay thế command1 theo số (sleep 5 && echo "Hello").

Hiện tại, command1 | false không thành công sau 5 giây nhưng không thành công ngay lập tức.

Hành vi này dường như có liên quan đến số lượng đầu ra mà lệnh tạo ra. Ví dụ: find/| false trả về ngay lập tức.

Nói chung, tôi tự hỏi tại sao bash cư xử như thế này. Bất cứ ai có thể tưởng tượng bất kỳ tình huống mà nó là hữu ích mà mã như command1 | non-existing-command không thoát ra cùng một lúc?

PS: Sử dụng tệp tạm thời không phải là một tùy chọn cho tôi, vì kết quả trung gian mà tôi sắp xếp sẽ lớn để được lưu trữ.

PPS: Không set -e cũng không set -o pipefail dường như ảnh hưởng đến hiện tượng này.

+1

Câu hỏi này phù hợp hơn với http://unix.stackexchange.com. Có thể bạn sẽ nhận được câu trả lời hay ở đó. – dogbane

Trả lời

16

Các tài liệu bash nói trong nó section about pipelines:

Mỗi lệnh trong một đường ống được thực hiện trong subshell riêng của mình [...]

"Trong subshell riêng của mình" có nghĩa là một mới bash quá trình được sinh ra, sau đó được thực hiện lệnh thực tế. Mỗi subshell bắt đầu thành công, ngay cả khi nó ngay lập tức xác định rằng lệnh được yêu cầu thực thi không tồn tại.

Điều này giải thích tại sao toàn bộ đường ống có thể được thiết lập thành công ngay cả khi một trong các lệnh là vô nghĩa. Bash không kiểm tra xem mỗi lệnh có thể được chạy hay không, nó ủy nhiệm cho các subshells. Điều đó cũng giải thích tại sao, ví dụ, lệnh nonexisting-command | touch hello sẽ ném một lỗi "không tìm thấy lệnh", nhưng tệp hello sẽ được tạo ra dù sao.

Trong phần tương tự, nó cũng nói:

Các chờ đợi vỏ cho tất cả các lệnh trong các đường ống để chấm dứt trước khi trở về một giá trị.

Trong sleep 5 | nonexisting-command, như A.H. chỉ ra, sleep 5 chấm dứt sau 5 giây, không phải ngay lập tức, vì thế mà vỏ cũng sẽ chờ 5   giây.

Tôi không biết tại sao triển khai được thực hiện theo cách này. Trong những trường hợp như của bạn, hành vi chắc chắn không phải là một trong những mong đợi.

Dù sao, một workaround hơi xấu xí là sử dụng FIFOs:

mkfifo myfifo 
./long-running-script.sh > myfifo & 
whoops-a-typo < myfifo 

Ở đây, long-running-script.sh được bắt đầu và sau đó là kịch bản thất bại ngay trên dòng tiếp theo. Sử dụng mutiple FIFO, điều này có thể được mở rộng đến các đường ống với nhiều hơn hai lệnh.

4

sleep 5 không tạo ra bất kỳ đầu ra nào cho đến khi kết thúc, trong khi find / ngay lập tức tạo ra đầu ra mà bash cố gắng để đường ống đến false.

+0

Đúng, câu hỏi là tốt nhưng có các ví dụ kém được chọn. –

+0

@Jaypal: Tôi đồng ý rằng ví dụ có thể gây hiểu nhầm. Tôi đã chỉnh sửa bài đăng và hy vọng rằng bài đăng đó rõ ràng hơn bây giờ. – Tobi

+0

@Dan: Có, nhưng điều này không thực sự trả lời câu hỏi của tôi. Tôi muốn biết liệu tôi có thể làm cho bash chấm dứt tất cả các lệnh trong một đường ống khi một lệnh đã thất bại. – Tobi

2

find/|false không thành công nhanh hơn vì cuộc gọi hệ thống write(2) đầu tiên từ find không thành công với lỗi EPIPE (Đường ống bị hỏng). Điều này là do false đã bị chấm dứt và do đó đường ống giữa hai lệnh này đã bị đóng ở một bên.

Nếu find sẽ bỏ qua lỗi đó (nó có thể làm như vậy trong lý thuyết) nó sẽ do "không chậm".

(sleep 5 && echo "Hello") | false là "không chạy chậm", bởi vì phần đầu tiên, sleep, không "kiểm tra" đường ống bằng cách ghi vào đó. Sau 5 giây, echo cũng gặp lỗi EPIPE. Cho dù lỗi này chấm dứt phần đầu tiên trong trường hợp này hay không thì không quan trọng đối với câu hỏi.

3

Chương trình đầu tiên không biết liệu lần thứ hai có bị chấm dứt hay không cho đến khi nó cố ghi một số ngày vào đường ống. Trong trường hợp lần thứ hai bị chấm dứt, người thứ nhất nhận được SIGPIPE thường gây ra lối thoát ngay lập tức.

Bạn có thể buộc các dòng đầu tiên của sản lượng được cấp nước tập trung ngay sau khi nhìn chằm chằm, như thế này:

(sleep 0.1; echo; command1) | command2 

100ms ngủ này được thiết kế để chờ cho đến khi thoát command2 thể ngay sau khi bắt đầu. Tất nhiên, nếu command2 thoát sau 2 giây, và lệnh1 sẽ im lặng trong 60 giây, toàn bộ lệnh shell sẽ chỉ trả về sau 60.1 giây.

Các vấn đề liên quan