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

340
Yajo

Я хочу определить, когда какой-то процесс застрял, но это не дает мне никаких подсказок по этому вопросу. Все, что я знаю, это то, что если он не выдает никакого вывода в течение заданного времени ожидания (скажем, 30 секунд), это означает, что он, вероятно, заблокирован.

Я знаю программу coreutils timeout, но она основана на времени полного завершения программы, а не на последней строке времени вывода. Я хотел бы, чтобы что-то подобное сработало:

timeout --stdout --stderr 30s my-program 

Так есть ли способ сделать это? Как мне это сделать?

2
Мне нравится идея, но я не могу указать на существующий инструмент. Это не должно быть трудно сделать само по себе. tripleee 6 лет назад 0
Вы можете найти [этот] (https://spin.atomicobject.com/2016/12/08/monitoring-stdout-with-a-timeout/) полезным. Wobbly 6 лет назад 0

3 ответа на вопрос

1
Kamil Maciorowski

Код

Сохраните это как tkill(сделайте его исполняемым и настройте, PATHесли необходимо):

#!/bin/bash  _terminate_children() { trap "exit 143" SIGTERM && kill -- -$$ }  trap _terminate_children SIGINT SIGTERM  tout="$1" shift eval $@ | tee >(while :; do read -t "$tout" case $? in 0) : ;; 1) break ;; *) _terminate_children ;; esac done) exit $ 

Основное использование

tkill 30 some_command 

Первый аргумент ( 30здесь) - время ожидания в секундах.


Заметки

  • tkillожидает some_commandгенерировать текстовый (не двоичный) вывод.
  • tkillзонды stdoutданной команды. Чтобы включить stderrперенаправление, как в последнем расширенном примере ниже.

Расширенное использование

Это действительные примеры:

tkill 9 foo -option value tkill 9 "foo -option value" # equivalent to the above tkill 5 "foo | bar" tkill 5 'foo | bar' tkill 5 'foo | bar | baz' # tkill monitors baz tkill 5 'foo | bar' | baz # baz reads from tkill tkill 3 "foo; bar" tkill 6 "foo && bar || baz" tkill 7 "some_command 2>&1" 

Используйте синтаксис Bash в этих цитатах.


Статус выхода

  • Если some_commandвыход сам по себе, то его выходной статус будет повторно использован как выходной статус tkill; tkill 5 trueвозвращается 0; tkill 5 falseвозвращается 1; tkill 5 "true; false"возвращается 1.
  • Если указанный тайм-аут истекает или tkillпрерывается, SIGINTили SIGTERMтогда статус выхода будет 143.

Фрагменты кода объяснены

  • eval делает возможными расширенные примеры.
  • teeпозволяет нам анализировать stdin, передавая его копию stdout.
  • read -t отвечает за применение тайм-аута, его статус выхода используется для определения того, что делать дальше.
  • Команды, которые отслеживаются, убиваются при необходимости с этим решением .
  • С помощью этого решения можно получить статус выхода отслеживаемой команды .
0
Ljm Dullaart

Итак, в основном примерно так:

#!/bin/bash tmp1=/tmp/tmp-$$-1 tmp2=/tmp/tmp-$$-2 touch $tmp1 touch $tmp2  time_out=30  typeset -i i  i=0 my-program > $tmp1 & pgmpid=$!  while ps $pgmpid > /dev/null ; do sleep 1 if diff $tmp1 $tmp2 > /dev/null ; then i=i+1 if [ $i -gt $time_out ] ; then kill $pgmpid fi else i=0 cp $tmp1 $tmp2  fi done  rm -f $tmp1 $tmp2 
Это должно сработать, хотя я ожидал что-то вроде однострочного ТБХ ... В любом случае, спасибо! Yajo 6 лет назад 0
0
Lubo Kanev

Запустите программу в фоновом режиме, одновременно копируя вывод в файл. Через 30 секунд, если файл пуст, закройте программу, иначе верните ее на передний план.

my-program | tee temp-file &  sleep 30 [ -s temp-file ] && kill $! || fg $! 
При этом, если через 30 секунд он замерзнет, ​​вам не повезло, верно? Yajo 6 лет назад 0
Да уж. Кажется, я неправильно понял вопрос. Lubo Kanev 6 лет назад 0