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



Я хотел бы автоматически убить команду через определенное время. Я имею в виду такой интерфейс:



% constrain 300 ./foo args


который будет работать "./foo " с "args", но автоматически убивает его, если он все еще работает через 5 минут.



было бы полезно обобщить идею на другие ограничения, такие как автозаполнение процесса, если он использует слишком много памяти.



существуют ли какие-либо существующие инструменты, которые это делают, или кто-нибудь написал такой вещь?



добавлено: решение Джонатана-это именно то, что я имел в виду, и оно работает как шарм на linux, но я не могу заставить его работать на Mac OSX. Я избавился от SIGRTMIN, который позволяет ему компилироваться нормально, но сигнал просто не отправляется в дочерний процесс. Кто-нибудь знает, как сделать эту работу на Mac?



[добавлено: обратите внимание, что обновление доступно от Джонатана, который работает на Mac и в другом месте.]

693   15  

15 ответов:

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

под *NIX, an alarm(2) наследуется через execve(2) и SIGALRM является фатальным по умолчанию. Так, часто можно просто:

$ doalarm () { perl -e 'alarm shift; exec @ARGV' "$@"; } # define a helper function

$ doalarm 300 ./foo.sh args

установить тривиальная обертка C чтобы сделать это для вас.

преимущества только один PID участвует, и механизм прост. Вы не убьете неправильный процесс, если, например, ./foo.sh вышел "слишком быстро", и его PID был повторно использован. Вам не нужно несколько подпроцессов оболочки, работающих совместно, что может быть сделано правильно, но довольно подвержено гонкам.

недостатки ограниченный по времени процесс не может управлять своим будильником (например,alarm(2),ualarm(2),setitimer(2)), так как это, вероятно, очевидно унаследованные тревоги. Очевидно, что он также не может блокировать или игнорировать SIGALRM, хотя то же самое можно сказать о SIGINT, SIGTERM и т. д. для некоторых других подходы.

некоторые (очень старые, я думаю) системы реализуют sleep(2) С точки зрения alarm(2), и, даже сегодня, некоторые программисты используют alarm(2) как грубый внутренний механизм тайм-аута для ввода-вывода и других операций. Однако, по моему опыту, этот метод применим к подавляющему большинству процессов, которые вы хотите ограничить по времени.

GNU Coreutils включает в себя автоотключение команда, установленная по умолчанию на многих системах.

https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html

посмотреть free -m в течение одной минуты, а затем убить его, отправив термин сигнал:

timeout 1m watch free -m

может быть, я не понимаю вопроса, но это звучит выполнимо напрямую, по крайней мере, в bash:

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

это запускает первую команду, в скобках, в течение пяти секунд, а затем убивает его. Вся операция выполняется синхронно, т. е. вы не сможете использовать свою оболочку, пока она занята ожиданием медленной команды. Если это не то, что вы хотели, должно быть возможно добавить еще один &.

The $! переменная-это встроенный Bash, который содержит идентификатор процесса последней запущенной дочерней оболочки. Важно не иметь & внутри скобки, делая это таким образом, теряет идентификатор процесса.

у меня есть программа под названием timeout что это написано в C, первоначально в 1989 году, но периодически обновляется.


Обновление: этот код не удается скомпилировать на MacOS X, потому что SIGRTMIN не определен, и не удается тайм-аут при запуске на MacOS X, потому что

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

ulimit -t 10

ограничивает процесс до 10 секунд процессорного времени.

чтобы фактически использовать его для ограничения нового процесса, а не текущего процесса, вы можете использовать сценарий-оболочку:

#! /usr/bin/env python

import os
os.system("ulimit -t 10; other-command-here")

other-command может быть любым инструментом. Я запускал версии Java, Python, C и Scheme различных алгоритмов сортировки и регистрировал, сколько времени они заняли, в то время как ограничение времени выполнения до 30 секунд. Приложение Cocoa-Python генерировало различные командные строки, включая Аргументы, и сопоставляло время в CSV-файл, но на самом деле это был просто пух поверх команды, представленной выше.

Perl один лайнер, просто для удовольствия:

perl -e '$s = shift; $SIG{ALRM} = sub { print STDERR "Timeout!\n"; kill INT => $p }; exec(@ARGV) unless $p = fork; alarm $s; waitpid $p, 0' 10 yes foo

это печатает ' foo ' в течение десяти секунд, а затем тайм-аут. Замените '10 'на любое количество секунд, а' yes foo ' - на любую команду.

команда timeout из Ubuntu / Debian при компиляции из исходного кода для работы на Mac. Дарвин

10.4.*

http://packages.ubuntu.com/lucid/timeout

моя вариация на perl one-liner дает вам статус выхода без mucking с fork () и wait () и без риска убить неправильный процесс:

#!/bin/sh
# Usage: timelimit.sh secs cmd [ arg ... ]
exec perl -MPOSIX -e '$SIG{ALRM} = sub { print "timeout: @ARGV\n"; kill(SIGTERM, -$$); }; alarm shift; $exit = system @ARGV; exit(WIFEXITED($exit) ? WEXITSTATUS($exit) : WTERMSIG($exit));' "$@"

в основном fork() и wait () скрыты внутри system (). SIGALRM доставляется родительскому процессу, который затем убивает себя и своего ребенка, отправляя SIGTERM всей группе процессов (-$$). В маловероятном случае, если дочерний выход и pid ребенка будет повторно использован до того, как произойдет kill (), это будет Не убивайте неправильный процесс, потому что новый процесс со старым дочерним pid не будет находиться в той же группе процессов родительского процесса perl.

в качестве дополнительного преимущества, скрипт также выходит с тем, что наверное правильный статус выхода.

попробуйте что-то вроде:

# This function is called with a timeout (in seconds) and a pid.
# After the timeout expires, if the process still exists, it attempts
# to kill it.
function timeout() {
    sleep 
    # kill -0 tests whether the process exists
    if kill -0  > /dev/null 2>&1 ; then
        echo "killing process "
        kill  > /dev/null 2>&1
    else
        echo "process  already completed"
    fi
}

<your command> &
cpid=$!
timeout 3 $cpid
wait $cpid > /dev/null 2>&
exit $?

это имеет обратную сторону, что если ваш процесс' pid повторно используется в течение таймаута, это может убить неправильный процесс. Это крайне маловероятно, но вы можете запускать 20000+ процессов в секунду. Это можно исправить.

Я использую "timelimit", который является пакетом, доступным в репозитории debian.

http://devel.ringlet.net/sysutils/timelimit/

небольшая модификация perl one-liner получит статус выхода справа.

perl -e '$s = shift; $SIG{ALRM} = sub { print STDERR "Timeout!\n"; kill INT => $p; exit 77 }; exec(@ARGV) unless $p = fork; alarm $s; waitpid $p, 0; exit ($? >> 8)' 10 yes foo

в принципе, выход ($? >> 8) будет пересылать код завершения подпроцесса. Я просто выбрал 77 в состоянии выхода для тайм-аута.

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

## run a command, aborting if timeout exceeded, e.g. timed-run 20 CMD ARGS ...
timed-run() {
  # timeout in seconds
  local tmout=""
  shift
  env CMD_TIMEOUT="$tmout" expect -f - "$@" <<"EOF"
# expect script follows
eval spawn -noecho $argv
set timeout $env(CMD_TIMEOUT)
expect {
   timeout {
      send_error "error: operation timed out\n"
      exit 1
   }
   eof
}
EOF
}

pure bash:


#!/bin/bash

if [[ $# < 2 ]]; then
  echo "Usage:  timeout cmd [options]"
  exit 1
fi

TIMEOUT=""
shift

BOSSPID=$$

(
  sleep $TIMEOUT
  kill -9 -$BOSSPID
)&
TIMERPID=$!

trap "kill -9 $TIMERPID" EXIT

eval "$@"
#!/bin/sh
( some_slow_task ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher

наблюдатель убивает медленную задачу после заданного тайм-аута; сценарий ждет медленной задачи и завершает наблюдатель.

примеры:

  • медленное выполнение задачи более 2 сек и было прекращено

медленная задача остановлена

( sleep 20 ) & pid=$!
( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "Slow task finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "Slow task interrupted"
fi
  • эта медленная задача завершена до заданного таймаута

медленная задание готовые

( sleep 2 ) & pid=$!
( sleep 20 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "Slow task finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "Slow task interrupted"
fi

нет ли способа установить определенное время с помощью "at" для этого?

$ at 05:00 PM kill -9 $pid

Кажется, намного проще.

Если вы не знаете, каким будет номер pid, я предполагаю, что есть способ прочитать его с помощью ps aux и grep, но не уверен, как это реализовать.

$   | grep someprogram
tony     11585  0.0  0.0   3116   720 pts/1    S+   11:39   0:00 grep someprogram
tony     22532  0.0  0.9  27344 14136 ?        S    Aug25   1:23 someprogram

ваш скрипт должен был бы прочитать pid и назначить ему переменную. Я не слишком опытный, но предположим, что это выполнимо.

Comments

    Ничего не найдено.