Тайм-аут команды в 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 ]

3 голосов
/ 01 февраля 2012

Если вы уже знаете имя программы (предположим, program), что она прекратит работу после истечения времени ожидания (например, 3 секунды), я могу предложить простое и несколько грязное альтернативное решение:

(sleep 3 && killall program) & ./program

Это прекрасно работает, если я вызываю тестовые процессы с помощью системных вызовов.

3 голосов
/ 28 июня 2017

Простой скрипт с ясностью кода. Сохранить в /usr/local/bin/run:

#!/bin/bash

# run
# Run command with timeout $1 seconds.

# Timeout seconds
timeout_seconds="$1"
shift

# PID
pid=$$

# Start timeout
(
  sleep "$timeout_seconds"
  echo "Timed out after $timeout_seconds seconds"
  kill -- -$pid &>/dev/null
) &
timeout_pid=$!

# Run
"$@"

# Stop timeout
kill $timeout_pid &>/dev/null

Тайм-аут команды, которая выполняется слишком долго:

$ run 2 sleep 10
Timed out after 2 seconds
Terminated
$

Заканчивается немедленно для команды, которая завершается:

$ run 10 sleep 2
$
2 голосов
/ 12 марта 2013

Существует также cratimeout Мартина Кракауэра (написано на C для систем Unix и Linux).

# cf. http://www.cons.org/cracauer/software.html
# usage: cratimeout timeout_in_msec cmd args
cratimeout 5000 sleep 1
cratimeout 5000 sleep 600
cratimeout 5000 tail -f /dev/null
cratimeout 5000 sh -c 'while sleep 1; do date; done'
1 голос
/ 30 июня 2015

OS X пока не использует bash 4 и не имеет / usr / bin / timeout, поэтому вот функция, которая работает в OS X без home-brew или macports, которая похожа на / usr / bin / timeout ( основываясь на ответе Тино). Проверка параметров, помощь, использование и поддержка других сигналов - упражнение для читателя.

# implement /usr/bin/timeout only if it doesn't exist
[ -n "$(type -p timeout 2>&1)" ] || function timeout { (
    set -m +b
    sleep "$1" &
    SPID=${!}
    ("${@:2}"; RETVAL=$?; kill ${SPID}; exit $RETVAL) &
    CPID=${!}
    wait %1
    SLEEPRETVAL=$?
    if [ $SLEEPRETVAL -eq 0 ] && kill ${CPID} >/dev/null 2>&1 ; then
      RETVAL=124
      # When you need to make sure it dies
      #(sleep 1; kill -9 ${CPID} >/dev/null 2>&1)&
      wait %2
    else
      wait %2
      RETVAL=$?
    fi
    return $RETVAL
) }
0 голосов
/ 16 февраля 2015

Очень упрощенный способ:

# command & sleep 5; pkill -9 -x -f "command"

с pkill (опция -f ) вы можете уничтожить вашу конкретную команду с аргументами или указать -n, чтобы избежать уничтожения старого процесса.

0 голосов
/ 03 октября 2018

У меня есть задание cron, которое вызывает php-скрипт, и иногда оно застревает в php-скрипте. Это решение было идеальным для меня.

Я использую:

scripttimeout -t 60 /script.php
0 голосов
/ 31 августа 2018

Опираясь на @ ответ loup ...

Если вы хотите приостановить процесс и отключить вывод задания / pid, выполните:

( (sleep 1 && killall program 2>/dev/null) &) && program --version 

Это помещает фоновый процесс в подоболочку, поэтому вы не видите вывод задания.

0 голосов
/ 06 апреля 2017

Вот версия, которая не основана на порождении дочернего процесса - мне нужен был автономный скрипт, в который встроены эти функции. Это также делает дробный интервал опроса, так что вы можете опрашивать быстрее. Тайм-аут был бы предпочтительным, но я застрял на старом сервере

# wait_on_command <timeout> <poll interval> command
wait_on_command()
{
    local timeout=$1; shift
    local interval=$1; shift
    $* &
    local child=$!

    loops=$(bc <<< "($timeout * (1 / $interval)) + 0.5" | sed 's/\..*//g')
    ((t = loops))
    while ((t > 0)); do
        sleep $interval
        kill -0 $child &>/dev/null || return
        ((t -= 1))
    done

    kill $child &>/dev/null || kill -0 $child &>/dev/null || return
    sleep $interval
    kill -9 $child &>/dev/null
    echo Timed out
}

slow_command()
{
    sleep 2
    echo Completed normally
}

# wait 1 sec in 0.1 sec increments
wait_on_command 1 0.1 slow_command

# or call an external command
wait_on_command 1 0.1 sleep 10
0 голосов
/ 27 марта 2009

В 99% случаев ответ НЕ для реализации какой-либо логики тайм-аута. Логика тайм-аута практически в любой ситуации является красным предупреждающим признаком того, что что-то иначе неверно и должно быть исправлено вместо .

Ваш процесс иногда зависает или прерывается через n секунд? Затем выясните причину и исправьте это.

Кроме того, для правильного решения проблемы strager вам нужно использовать wait "$ SPID" вместо fg 1, так как в сценариях у вас нет управления заданиями (и пытаться включить его глупо). Более того, fg 1 опирается на тот факт, что вы ранее не запускали в скрипте другие задания, что является неверным предположением.

0 голосов
/ 16 июля 2015

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

Теперь я использую следующее:

ssh server '( sleep 60 && kill -9 0 ) 2>/dev/null & my_command; RC=$? ; sleep 1 ; pkill -P $! ; exit $RC'

Таким образом, команда возвращает 255, когда был тайм-аут или код возврата команды в случае успеха

Обратите внимание, что уничтожение процессов из ssh-сессии обрабатывается не так, как в интерактивной оболочке. Но вы также можете использовать опцию -t для ssh для выделения псевдотерминала, поэтому он действует как интерактивная оболочка

...