Как ввести тайм-аут для сценариев оболочки?

35

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

Как я могу ввести тайм-аут для всего сценария оболочки под SuSE?

Радек
источник
4
Это очень расплывчатый вопрос. Можете ли вы уточнить, какой таймаут? Вам нужен тайм-аут для всего сценария или тайм-аут для отдельной функции или команды? Что ты пытаешься сделать?
Тим Кеннеди
@TimKennedy: надеюсь, теперь лучше.
Радек
1
См. Также Выдержка из сценария оболочки (я не считаю его дубликатом из-за моего требования переносимости, которое исключает такие инструменты, как timeoutили expect)
Жиль "ТАК - перестать быть злым"

Ответы:

30

Если GNU timeoutнедоступен, вы можете использовать его expect(Mac OS X, BSD, ... обычно не имеют инструментов и утилит GNU по умолчанию).

################################################################################
# Executes command with a timeout
# Params:
#   $1 timeout in seconds
#   $2 command
# Returns 1 if timed out 0 otherwise
timeout() {

    time=$1

    # start the command in a subshell to avoid problem with pipes
    # (spawn accepts one command)
    command="/bin/sh -c \"$2\""

    expect -c "set echo \"-noecho\"; set timeout $time; spawn -noecho $command; expect timeout { exit 1 } eof { exit 0 }"    

    if [ $? = 1 ] ; then
        echo "Timeout after ${time} seconds"
    fi

}

Пример редактирования :

timeout 10 "ls ${HOME}"
Matteo
источник
Выглядит хорошо. Как я могу увидеть результат выполнения команды на экране?
Радек
Привет, вывод должен быть видимым, так как stdout нигде не перенаправлен. Я добавил пример для использования. В моем случае он печатает содержимое моего домашнего каталога, как и ожидалось. Какая команда не печатает вывод?
Маттео
Я не назвал это правильно. Работает просто отлично! Спасибо. Только он не выводит количество секунд после тайм-аута.
Радек
@Radek Извините, исправлено
Маттео
1
Я хотел бы подчеркнуть, что при использовании тайм-аута GNU сам статус возврата timeoutравен 124 в случае тайм-аута, в противном случае это статус возврата команды (это упоминается в ответе Тима Кеннеди).
QuasarDonkey
15

Спасибо тебе за пояснение.

Самый простой способ выполнить то, что вам нужно, - это запустить ваш скрипт с циклом внутри оболочки, как timeoutкоманда из пакета GNU Coreutils.

root@coraid-sp:~# timeout --help            
Usage: timeout [OPTION] DURATION COMMAND [ARG]...
   or: timeout [OPTION]
Start COMMAND, and kill it if still running after DURATION.

Mandatory arguments to long options are mandatory for short options too.
  -k, --kill-after=DURATION
                   also send a KILL signal if COMMAND is still running
                   this long after the initial signal was sent.
  -s, --signal=SIGNAL
                   specify the signal to be sent on timeout.
                   SIGNAL may be a name like 'HUP' or a number.
                   See `kill -l` for a list of signals
      --help     display this help and exit
      --version  output version information and exit

DURATION is an integer with an optional suffix:
`s' for seconds(the default), `m' for minutes, `h' for hours or `d' for days.

If the command times out, then exit with status 124.  Otherwise, exit
with the status of COMMAND.  If no signal is specified, send the TERM
signal upon timeout.  The TERM signal kills any process that does not
block or catch that signal.  For other processes, it may be necessary to
use the KILL (9) signal, since this signal cannot be caught.

Report timeout bugs to bug-coreutils@gnu.org
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
For complete documentation, run: info coreutils 'timeout invocation'

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

Тим Кеннеди
источник
Я не сразу понял, что у меня это установлено, потому что оно вызывается gtimeoutна моей установке (OSX).
Джошуа Голдберг
7

Запустите сторожевой процесс из вашего скрипта, чтобы убить его родителя, если он выполняется слишком долго. Пример:

# watchdog process
mainpid=$$
(sleep 5; kill $mainpid) &
watchdogpid=$!

# rest of script
while :
do
   ...stuff...
done
kill $watchdogpid

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

Кайл Джонс
источник
6
Но вам придется каждый раз ждать таймаута. В вашем примере ваш скрипт всегда будет работать 5 секунд, даже если он намного короче. Вам также следует сохранить PID сторожевого таймера и убить его в конце.
Маттео
@ Маттео Достаточно справедливо. Добавлен.
Кайл Джонс
1
Обратите внимание, что это может быть более сложным, чем это, в зависимости от того, что ... делает ... делает. См. « Время» в сценарии оболочки
Жиль: «Хватит быть злым»
Читайте также: сколько времени PID могут считаться уникальными stackoverflow.com/questions/11323410/linux-pid-recycling
Флориан Кастеллан
3

Там также cratimeoutМартин Кракауэр.

# cf. http://www.cons.org/cracauer/software.html
# usage: cratimeout timeout_in_msec cmd args
cratimeout 5000 sleep 600
cratimeout 5000 tail -f /dev/null
cratimeout 5000 sh -c 'while sleep 1; do date; done'
jackz
источник
2
#!/bin/sh

# Execute a command with a timeout

if [ "$#" -lt "2" ]; then
echo "Usage:   `basename $0` timeout_in_seconds command" >&2
echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
exit 1
fi

cleanup()
{
trap - ALRM               #reset handler to default
kill -ALRM $a 2>/dev/null #stop timer subshell if running
kill $! 2>/dev/null &&    #kill last job
  exit 124                #exit with 124 if it was running
}

watchit()
{
trap "cleanup" ALRM
sleep $1& wait
kill -ALRM $$
}

watchit $1& a=$!         #start the timeout
shift                    #first param was timeout for sleep
trap "cleanup" ALRM INT  #cleanup after timeout
"$@"& wait $!; RET=$?    #start the job wait for it and save its return value
kill -ALRM $a            #send ALRM signal to watchit
wait $a                  #wait for watchit to finish cleanup
exit $RET                #return the value
Вивек Рагупати
источник