Тайм-аут команды в bash без лишней задержки - PullRequest
254 голосов
/ 27 марта 2009

Этот ответ на Команда командной строки для автоматического уничтожения команды через определенное время

предлагает 1-строчный метод для тайм-аута длительной команды из командной строки bash:

( /path/to/slow command with options ) & sleep 5 ; kill $!

Но возможно, что данная "длительная" команда может завершиться раньше, чем время ожидания. (Давайте назовем это «обычно продолжительной, но иногда быстрой» командой или tlrbsf для развлечения.)

Так что у этого изящного подхода с 1 линией есть пара проблем. Во-первых, sleep не является условным, поэтому устанавливает нежелательную нижнюю границу времени, необходимого для завершения последовательности. Рассмотрим 30 с, 2 м или даже 5 м для сна, когда команда tlrbsf завершится через 2 секунды & mdash; крайне нежелательно. Во-вторых, kill является безусловным, поэтому эта последовательность попытается убить не запущенный процесс и скулить об этом.

Итак ...

Есть ли способ для тайм-аута обычно продолжительной, но иногда быстрой ( "tlrbsf" ) команды,

  • имеет реализацию bash (на другой вопрос уже есть ответы на Perl и C)
  • завершится в более раннем из двух: tlrbsf завершение программы или истекло время ожидания
  • не будет уничтожать несуществующие / не запущенные процессы (или, опционально: не будет жаловаться на неудачное уничтожение)
  • не обязательно должен быть 1-строчным
  • может работать под Cygwin или Linux

... и, для получения бонусных очков, запускает команду tlrbsf на переднем плане и любой «спящий» или дополнительный процесс в фоновом режиме, такой как stdin / stdout / stderr Команда tlrbsf может быть перенаправлена ​​так же, как если бы она была запущена напрямую?

Если это так, пожалуйста, поделитесь своим кодом. Если нет, объясните, пожалуйста, почему.

Я потратил некоторое время, пытаясь взломать вышеупомянутый пример, но я достиг предела своих навыков в bash.

Ответы [ 22 ]

0 голосов
/ 04 января 2013
#! /bin/bash
timeout=10
interval=1
delay=3
(
    ((t = timeout)) || :

    while ((t > 0)); do
        echo "$t"
        sleep $interval
        # Check if the process still exists.
        kill -0 $$ 2> /dev/null || exit 0
        ((t -= interval)) || :
    done

    # Be nice, post SIGTERM first.
    { echo SIGTERM to $$ ; kill -s TERM $$ ; sleep $delay ; kill -0 $$ 2> /dev/null && { echo SIGKILL to $$ ; kill -s KILL $$ ; } ; }
) &

exec "$@"
0 голосов
/ 11 января 2013

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

#!/usr/bin/env bash

safe_kill()
{
  ps aux | grep -v grep | grep $1 >/dev/null && kill ${2:-} $1
}

my_timeout()
{
  typeset _my_timeout _waiter_pid _return
  _my_timeout=$1
  echo "Timeout($_my_timeout) running: $*"
  shift
  (
    trap "return 0" USR1
    sleep $_my_timeout
    echo "Timeout($_my_timeout) reached for: $*"
    safe_kill $$
  ) &
  _waiter_pid=$!
  "$@" || _return=$?
  safe_kill $_waiter_pid -USR1
  echo "Timeout($_my_timeout) ran: $*"
  return ${_return:-0}
}

my_timeout 3 cd scripts
my_timeout 3 pwd
my_timeout 3 true  && echo true || echo false
my_timeout 3 false && echo true || echo false
my_timeout 3 sleep 10
my_timeout 3 pwd

с выходами:

Timeout(3) running: 3 cd scripts
Timeout(3) ran: cd scripts
Timeout(3) running: 3 pwd
/home/mpapis/projects/rvm/rvm/scripts
Timeout(3) ran: pwd
Timeout(3) running: 3 true
Timeout(3) ran: true
true
Timeout(3) running: 3 false
Timeout(3) ran: false
false
Timeout(3) running: 3 sleep 10
Timeout(3) reached for: sleep 10
Terminated

конечно, я предполагаю, что был каталог под названием scripts

...