Chấm dứt một vòng lặp vô hạn


52

Tôi có một lệnh mà tôi muốn chạy lại tự động mỗi khi nó kết thúc, vì vậy tôi đã chạy một cái gì đó như thế này:

while [ 1 ]; do COMMAND; done;

nhưng nếu tôi không thể dừng vòng lặp Ctrl-cvì điều đó sẽ giết chết COMMANDchứ không phải toàn bộ vòng lặp.

Làm thế nào tôi có thể đạt được một cái gì đó tương tự nhưng tôi có thể dừng lại mà không cần phải đóng thiết bị đầu cuối?


11
Nếu tôi gặp rắc rối, tôi chỉ cần sử dụng Ctrl-Z để dừng công việc và sau đó "giết% 1" để giết nó.
Paul Cager

3
Chỉ cần chờ đợi ... Linus đã được trích dẫn khi nói: xông Tất cả chúng ta đều biết Linux rất tuyệt ... nó có các vòng lặp vô hạn trong 5 giây. Hãy - thật sự ... chỉ cần chờ thêm vài giây nữa, nó sẽ hoàn thành.
lornix

@PaulCager cũng làm việc cho tôi! Tại sao nó không hoạt động khi Ctrl-C không hoạt động?
Ciro Santilli 新疆 心 心

@cirosantilli nó giết chết công việc bên ngoài (bash "Wrapper"). Trong một số trường hợp, nó sẽ không ngay lập tức giết "LỰA CHỌN", ví dụ, nếu bạn làm nền cho nó, nó có thể lẻn qua sống ngay cả khi cha mẹ nó đã chết. Nhưng vòng lặp đã chết, và đó là phần quan trọng.
orion

Câu trả lời:


37

Kiểm tra trạng thái thoát của lệnh. Nếu lệnh bị chấm dứt bởi tín hiệu, mã thoát sẽ là 128 + số tín hiệu. Từ tài liệu trực tuyến GNU cho bash :

Đối với mục đích của shell, một lệnh thoát với trạng thái thoát 0 đã thành công. Một trạng thái thoát khác không chỉ ra thất bại. Lược đồ dường như phản trực giác này được sử dụng vì vậy có một cách được xác định rõ để chỉ ra thành công và nhiều cách khác nhau để chỉ ra các chế độ thất bại khác nhau. Khi một lệnh kết thúc trên tín hiệu gây tử vong có số là N, Bash sử dụng giá trị 128 + N làm trạng thái thoát.

POSIX cũng chỉ định rằng giá trị của lệnh bị chấm dứt bởi tín hiệu lớn hơn 128, nhưng dường như không chỉ định giá trị chính xác của nó giống như GNU:

Trạng thái thoát của lệnh kết thúc vì nhận được tín hiệu sẽ được báo cáo là lớn hơn 128.

Ví dụ: nếu bạn ngắt một lệnh bằng control-C, mã thoát sẽ là 130, vì SIGINT là tín hiệu 2 trên các hệ thống Unix. Vì thế:

while [ 1 ]; do COMMAND; test $? -gt 128 && break; done

9
Cần phải đề cập rằng điều này không được đảm bảo, trên thực tế, nhiều ứng dụng sẽ không làm điều này.
Chris Down

1
@Kyle Jones: bạn có thể liên kết đến các tài liệu POSIX / GNU có đề cập đến điều đó không?
Ciro Santilli 新疆 心 心

@cirosantilli Xong.
Kyle Jones

@KyleJones cảm ơn! Vẫn còn trong thực tế không hoạt động cho LỆNH = paplay alert.ogg, có lẽ vì paplayxử lý tín hiệu?
Ciro Santilli 心 心 事件 事件

@cirosantilli Vâng, đó là lý do. Nếu một quy trình xử lý tín hiệu và thoát, điều đó khác với quy trình bị chấm dứt bởi tín hiệu chưa được xử lý.
Kyle Jones

48

Bạn có thể dừng và đặt công việc của bạn ở chế độ nền trong khi nó đang chạy bằng ctrl+ z. Sau đó, bạn có thể giết công việc của mình với:

$ kill %1

Trong đó [1] là số công việc của bạn.


Xem thêm câu trả lời này để giải thích và nhiều hơn nữa.
Skippy le Grand Gourou

2
Câu trả lời tương đối gần đây chỉ đơn giản là hoạt động. Cần phải được nâng cao. +1
shivams

Bạn đã giúp tôi rất nhiều. Đây là những gì tôi đã tìm kiếm trong câu hỏi này :)
Maximilian Ruta

18

Tôi có thể nói rằng tốt nhất là đặt vòng lặp vô hạn của bạn vào một tập lệnh và xử lý các tín hiệu ở đó. Đây là một điểm khởi đầu cơ bản . Tôi chắc chắn bạn sẽ muốn sửa đổi nó cho phù hợp. Kịch bản sử dụng trapđể bắt ctrl- c(hoặc SIGTERM), tắt lệnh (Tôi đã sử dụng sleepở đây để kiểm tra) và thoát.

cleanup ()
{
kill -s SIGTERM $!
exit 0
}

trap cleanup SIGINT SIGTERM

while [ 1 ]
do
    sleep 60 &
    wait $!
done

4
Đẹp. Đây là cách tôi sử dụng mẹo này để tạo một trình bao bọc netcat tự động khởi động:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Douglas

2
nếu bạn thêm trapcách tiếp cận này vào cùng một tập lệnh (bash) với vòng lặp vô hạn sẽ bị giết, hãy sử dụng $$thay vì $!(xem tại đây )
ardew

15

Tôi nói chung chỉ cần giữ Ctrl-C. Sớm hay muộn nó sẽ đăng ký giữa COMMANDvà do đó chấm dứt whilevòng lặp. Có lẽ có một cách tốt hơn.


1
Không chắc chắn tại sao, nhưng nó thất bại đối với một số LỰA CHỌN như paplaytrên tệp 1s.
Ciro Santilli 新疆 心 心

Nó làm việc cho tôi
alexandr

Đó là lực lượng vũ phu của tất cả các giải pháp ở đây. : /
markroxor

8

Nếu bạn chạy bash với -enó sẽ thoát trong mọi điều kiện lỗi:

#!/bin/bash -e
false # returns 1
echo This won't be printed

7

Tại sao không đơn giản,

while [ 1 ]; do COMMAND || break; done;

Hoặc khi được sử dụng trong một tập lệnh,

#!/bin/bash
while [ 1 ]; do
  # ctrl+c terminates COMMAND and exits the while loop
  # (assuming COMMAND responds to ctrl+c)
  COMMAND || break
done;

1
Giải pháp rất thanh lịch. Nhưng điều này sẽ chỉ hoạt động nếu LỰA CHỌN luôn trả về trạng thái thoát thành công?
howardh

Có @howardh, điều đó đúng.
Dale Anderson

3

Tôi thích một giải pháp khác:

touch .runcmd; while [ -f ".runcmd" ]; do COMMAND; sleep 1; done

Để giết vòng lặp, chỉ cần làm:

rm .runcmd && kill `pidof COMMAND`

2
  1. Bạn luôn có thể giết một tiến trình bằng cách sử dụng PID của nó, không cần phải đóng thiết bị đầu cuối của bạn
  2. Nếu bạn muốn chạy một cái gì đó trong một vòng lặp vô hạn như một daemon thì tốt nhất bạn nên đặt nó vào nền
  3. while : sẽ tạo ra một vòng lặp vô hạn và giúp bạn tiết kiệm bằng cách viết [ 1 ]

    while :; do COMMAND; done &

Điều này sẽ in ra PID. Nếu bạn thoát khỏi lời nhắc của mình bằng cách sử dụng ctrl+dthì công việc nền sẽ không thoát và sau đó bạn có thể giết công việc đó từ mọi nơi bằng cách sử dụngkill PID

Nếu bạn mất dấu vết của PID, bạn có thể sử dụng pstree -pa $USERhoặc pgrep -fl '.*PROCESS.*'để giúp bạn tìm thấy nó


0

Sử dụng trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
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.