Tại sao không có bash trong khi thoát khỏi vòng lặp khi đường ống kết thúc tiểu ban?


12

Tại sao lệnh dưới đây không thoát? Thay vì thoát ra, vòng lặp chạy vô thời hạn.

Trong khi tôi phát hiện ra hành vi này bằng cách sử dụng một thiết lập phức tạp hơn, hình thức đơn giản nhất của lệnh sẽ giảm xuống như sau.

Không thoát:

while /usr/bin/true ; do echo "ok" | cat ; done | exit 1

Không có lỗi chính tả ở trên. Mỗi '|' là một đường ống. 'Lối ra 1' là viết tắt của một quá trình khác đã chạy và thoát.

Tôi hy vọng "lối ra 1" sẽ gây ra SIGPIPE trên vòng lặp while (ghi trên đường ống không có đầu đọc) và để vòng lặp thoát ra. Nhưng, vòng lặp tiếp tục chạy.

Tại sao lệnh không dừng lại?


zsh thoát bình thường.
Braiam

Câu trả lời:


13

Đó là do một sự lựa chọn trong việc thực hiện.

Chạy cùng một kịch bản trên Solaris với việc ksh93tạo ra một hành vi khác:

$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]

Điều gây ra vấn đề là đường ống bên trong, không có nó, vòng lặp sẽ thoát khỏi bất kỳ shell / OS nào:

$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$

cat đang nhận được tín hiệu SIGPIPE dưới bash nhưng shell vẫn lặp lại vòng lặp.

Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

Tài liệu Bash nêu rõ:

Shell chờ tất cả các lệnh trong đường ống kết thúc trước khi trả về một giá trị.

Tài liệu Ksh nêu:

Mỗi lệnh, ngoại trừ có thể là lệnh cuối cùng, được chạy như một tiến trình riêng biệt; Shell chờ lệnh cuối cùng kết thúc.

POSIX tuyên bố:

Nếu đường ống không ở chế độ nền (xem Danh sách không đồng bộ), trình bao sẽ đợi lệnh cuối cùng được chỉ định trong đường ống hoàn thành và cũng có thể đợi tất cả các lệnh hoàn tất.


Tôi nghĩ rằng nó không chính xác là đường ống bên trong gây ra vấn đề, nó được xây dựng echobỏ qua SIGPIPE. Bạn cũng có thể tái tạo vấn đề bằng cách sử dụng env echothay vì echo(để buộc echonhị phân thực tế được sử dụng). (So ​​sánh cũng là đầu ra của { echo hi; echo $? >&2; } | exit 1{ env echo hi; echo $? >&2; } | exit 1.)
Lucas Werkmeister

1

Vấn đề này đã làm tôi khó chịu trong nhiều năm. Cảm ơn jilliagre vì đã đi đúng hướng.

Đặt lại câu hỏi một chút, trên hộp linux của tôi, điều này thoát như mong đợi:

while true ; do echo "ok"; done | head

Nhưng nếu tôi thêm một đường ống, nó sẽ không thoát như mong đợi:

while true ; do echo "ok" | cat; done | head

Điều đó làm tôi thất vọng trong nhiều năm. Bằng cách xem xét câu trả lời được viết bởi jilliagre, tôi đã đưa ra cách khắc phục tuyệt vời này:

while true ; do echo "ok" | cat || exit; done | head

QED ...

Vâng, không hoàn toàn. Đây là một cái gì đó phức tạp hơn một chút:

i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' || exit
done | head

Điều này không hoạt động đúng. Tôi đã thêm vào || exitđể nó biết cách chấm dứt sớm, nhưng lần đầu tiên echokhông khớp với nhau grepnên vòng lặp thoát ngay lập tức. Trong trường hợp này, bạn thực sự không quan tâm đến trạng thái thoát của grep. My work-around là bổ sung thêm cat. Vì vậy, đây là một kịch bản được gọi là "hàng chục":

#!/bin/bash
i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' | cat || exit
done

Điều này kết thúc đúng khi chạy như tens | head. Cảm ơn Chúa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.