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


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.