Команда командной строки для автоматического уничтожения команды через определенное время
Я хотел бы автоматически убить команду через определенное время. Я имею в виду такой интерфейс:
% constrain 300 ./foo args
который будет работать "./foo " с "args", но автоматически убивает его, если он все еще работает через 5 минут.
было бы полезно обобщить идею на другие ограничения, такие как автозаполнение процесса, если он использует слишком много памяти.
существуют ли какие-либо существующие инструменты, которые это делают, или кто-нибудь написал такой вещь?
добавлено: решение Джонатана-это именно то, что я имел в виду, и оно работает как шарм на linux, но я не могу заставить его работать на Mac OSX. Я избавился от SIGRTMIN, который позволяет ему компилироваться нормально, но сигнал просто не отправляется в дочерний процесс. Кто-нибудь знает, как сделать эту работу на Mac?
[добавлено: обратите внимание, что обновление доступно от Джонатана, который работает на Mac и в другом месте.]
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.*
моя вариация на 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+ процессов в секунду. Это можно исправить.
небольшая модификация 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