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

52

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

while [ 1 ]; do COMMAND; done;

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

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

howardh
источник
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
ephsmith
источник
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цикл. Может быть, есть лучший способ.

jw013
источник
1
Не уверен, почему, но он не работает для некоторых команд, как paplayв файле 1s.
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
У меня это сработало
александр
Это грубая сила всех решений здесь. : /
Маркроксор
8

Если вы запустите bash с -eним, то выйдете из-за ошибок:

#!/bin/bash -e
false # returns 1
echo This won't be printed
Восстановить Монику
источник
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`
M4tze
источник
2
  1. Вы всегда можете убить процесс, используя его PID, нет необходимости закрывать терминал
  2. Если вы хотите запустить что-то в бесконечном цикле, например, демон, лучше всего поместить это в фоновый режим
  3. while : создаст бесконечный цикл и спасет вас от написания [ 1 ]

    while :; do COMMAND; done &

Это напечатает PID. Если вы выйдете из своего приглашения с помощью, ctrl+dтогда фоновое задание не будет завершено, и вы сможете позже убить задание из любого места, используяkill PID

Если вы потеряли отслеживание вашего PID, вы можете использовать pstree -pa $USERили, pgrep -fl '.*PROCESS.*'чтобы помочь вам найти его

errant.info
источник
0

Используйте trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
markroxor
источник