Завершение бесконечного цикла


52

У меня есть команда, которую я хочу запускать автоматически каждый раз, когда она завершается, поэтому я запустил что-то вроде этого:

while [ 1 ]; do COMMAND; done;

но если я не могу остановить цикл, так Ctrl-cкак это просто убивает, COMMANDа не весь цикл.

Как мне добиться чего-то подобного, но что я могу остановить, не закрывая терминал?


11
Если я в bash, я просто использую Ctrl-Z, чтобы остановить задание, а затем «kill% 1», чтобы завершить его.
Пол Кейджер

3
Просто подождите ... Линус сказал: «Мы все знаем, что Linux великолепен ... он делает бесконечные циклы за 5 секунд». Так что на самом деле ... просто подождите еще несколько секунд, он должен завершиться.
Lornix

@PaulCager работал и для меня! Почему это работает, когда Ctrl-C не работает?
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件

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

Ответы:


38

Проверьте статус выхода команды. Если команда была прервана сигналом, код выхода будет 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
@ Кайл Джонс: можете ли вы ссылаться на документы POSIX / GNU, которые упоминают об этом?
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件

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

@KyleJones спасибо! Все еще на практике не работает для COMMAND = paplay alert.ogg, возможно, потому что paplayобрабатывает сигнал?
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件

@cirosantilli Да, это причина. Если процесс обрабатывает сигнал и выходит, это отличается от процесса, который завершается необработанным сигналом.
Кайл Джонс

49

Вы можете остановиться и поставить свою работу в фоновом режиме, пока она выполняется с помощью ctrl+ z. Тогда вы можете убить свою работу с:

$ kill %1

Где [1] - номер вашей работы.


Смотрите также этот ответ для объяснений и многое другое.
Скиппи ле Гран Гуру

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
Приятно. Вот как я использовал этот совет для создания автозапускающейся оболочки netcat: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.
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件

У меня это сработало
александр

Это грубая сила всех решений здесь. : /
Маркроксор


8

Почему бы не просто,

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.