Закінчення нескінченної петлі


52

У мене є команда про те, що я хочу запускатись автоматично автоматично кожного разу, коли він закінчується, тому я запустив щось подібне:

while [ 1 ]; do COMMAND; done;

але якщо я не можу зупинити цикл, Ctrl-cоскільки це просто вбиває, COMMANDа не весь цикл.

Як я можу домогтися чогось подібного, але що можу зупинити, не закриваючи термінал?


11
Якщо я в баші, я просто використовую Ctrl-Z, щоб зупинити роботу, а потім "вбити% 1", щоб її вбити.
Пол Кагер

3
Тільки зачекайте ... Лінус процитував: "Ми всі знаємо, що Linux чудовий ... він робить нескінченні петлі за 5 секунд". - Так справді ... просто зачекайте ще кілька секунд, і це має завершитися.
lornix

@PaulCager теж працював на мене! Чому він працює там, де Ctrl-C не працює?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

@cirosantilli це вбиває зовнішню роботу (bash "обгортка"). У деяких ситуаціях він не вбиває негайно "КОМАНДУ", наприклад, якщо ви перетворите його на екран, він може проникнути повз живого, навіть якщо його батько помер. Але петля мертва, і це важлива частина.
Оріон

Відповіді:


37

Перевірте стан виходу команди. Якщо команда була припинена сигналом, код виходу буде 128 + номер сигналу. З онлайн-документації GNU для bash :

Для цілей оболонки вдалася команда, яка виходить зі статусом нульового виходу. Ненульовий статус виходу вказує на збій. Ця, здавалося б, контр-інтуїтивна схема використовується, тому існує один чітко визначений спосіб вказувати на успіх і різноманітні способи вказувати різні режими відмов. Коли команда закінчується на фатальному сигналі, число якого N, Bash використовує значення 128 + N як статус виходу.

POSIX також вказує, що значення команди, що закінчується сигналом, перевищує 128, але, схоже, не вказує її точне значення, як це робить GNU:

Стан виходу команди, яка припинилась через отримання сигналу, повідомляється як 128.

Наприклад, якщо ви перериваєте команду з control-C, код виходу буде 130, оскільки SIGINT - це сигнал 2 у системах Unix. Тому:

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

9
Слід зазначити, що це не гарантується, насправді багато додатків цього не робитимуть.
Кріс Даун

1
@Kyle Jones: чи можете ви зв’язатись із документами POSIX / GNU, які згадують про це?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

@cirosantilli Готово.
Кайл Джонс

@KyleJones дякую! Ще на практиці не працює для COMMAND = paplay alert.ogg, можливо, тому що paplayобробляє сигнал?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

@cirosantilli Так, це причина. Якщо процес обробляє сигнал і припиняє роботу, це відрізняється від процесу, який закінчується необробленим сигналом.
Кайл Джонс

48

Ви можете зупинитись і поставити свою роботу у фоновий режим, поки вона працює за допомогою ctrl+ z. Тоді ви можете вбити свою роботу за допомогою:

$ kill %1

Де [1] - ваш номер роботи.


Дивіться також цю відповідь для пояснень та іншого.
Skippy le Grand Gourou

2
Ця відносно недавня відповідь просто працює. Потрібно звернути увагу на потреби. +1
шивам

Ти мені дуже допоміг. Це те, що я шукав у цьому питанні :)
Максиміліан Рута

18

Я б сказав, що може бути найкраще помістити свій нескінченний цикл у сценарій і обробляти сигнали там. Ось основна відправна точка . Я впевнений, що вам захочеться змінити його відповідно до цього. Сценарій використовує trapдля лову ctrl- c(або SIGTERM), вбиває команду (я sleepтут використовувався як тест) і виходить.

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

trap cleanup SIGINT SIGTERM

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

4
Приємно. Ось як я використав цю пораду для автоматичного запуску сітчастої обгортки:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Дуглас

2
якщо ви додасте цей trapпідхід до того ж (bash) сценарію з нескінченним циклом, який потрібно вбити, використовуйте $$замість $!(див. тут )
ardnew

15

Я взагалі просто тримаюсь Ctrl-C. Рано чи пізно він зареєструється між COMMAND's і тим самим припинить whileцикл. Можливо, є кращий спосіб.


1
Не впевнений чому, але це не вдається для певних КОМАНДІВ, як paplayу файлі 1s.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Це працювало для мене
Олександр

Ось така груба сила всіх рішень тут. : /
markroxor


7

Чому б не просто,

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

Або коли використовується в сценарії,

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

1
Дуже елегантне рішення. Але хіба це не спрацює, лише якщо COMMAND завжди повертає статус успішного виходу?
howardh

Так @howardh, це правильно.
Дейл Андерсон

3

Я віддаю перевагу іншому рішенню:

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

Для того, щоб вбити цикл, просто зробіть:

rm .runcmd && kill `pidof COMMAND`

2
  1. Ви завжди можете вбити процес за допомогою PID, не потрібно закривати свій термінал
  2. Якщо ви хочете запустити щось у нескінченному циклі, як демон, то найкраще покласти це на другий план
  3. while : створить нескінченний цикл і заощадить ваші записи [ 1 ]

    while :; do COMMAND; done &

Це надрукує PID. Якщо ви вийдете з підказки, використовуючи ctrl+dфонове завдання, не вийдете, і ви можете пізніше вбити його з будь-якого місцяkill PID

Якщо ви втратите інформацію про свій PID, ви можете скористатися ним pstree -pa $USERабо pgrep -fl '.*PROCESS.*'допомогти знайти його


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.