Команда командной строки для автоматического уничтожения команды через определенное время - PullRequest
52 голосов
/ 02 марта 2009

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

% constrain 300 ./foo args

Который запускает «./foo» с «args», но автоматически убивает его, если он все еще работает через 5 минут.

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

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

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

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

Ответы [ 15 ]

2 голосов
/ 12 февраля 2010

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

# 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 $1
    # kill -0 tests whether the process exists
    if kill -0 $2 > /dev/null 2>&1 ; then
        echo "killing process $2"
        kill $2 > /dev/null 2>&1
    else
        echo "process $2 already completed"
    fi
}

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

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

1 голос
/ 31 августа 2010

Разве нет способа установить определенное время с помощью "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 и присвоить ему переменную. Я не слишком опытен, но предположим, что это выполнимо.

1 голос
/ 16 марта 2010

чистый удар:


#!/bin/bash

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

TIMEOUT="$1"
shift

BOSSPID=$$

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

trap "kill -9 $TIMERPID" EXIT

eval "$@"
1 голос
/ 18 февраля 2010

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

## run a command, aborting if timeout exceeded, e.g. timed-run 20 CMD ARGS ...
timed-run() {
  # timeout in seconds
  local tmout="$1"
  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
}
1 голос
/ 11 октября 2009

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

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

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

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