문제

이 답변 에게 일정 시간이 지나면 명령을 자동 종료하는 명령줄 명령

bash 명령줄에서 장기 실행 명령을 시간 초과하는 한 줄 방법을 제안합니다.

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

그러나 특정 "장기 실행" 명령이 시간 초과보다 일찍 완료될 수도 있습니다.("일반적으로 오래 실행되지만 때로는 빠른" 명령이라고 부르겠습니다. tlrbsf 재미로.)

따라서 이 멋진 1-liner 접근 방식에는 몇 가지 문제가 있습니다.첫째, sleep 조건부가 아니므로 시퀀스가 ​​완료되는 데 걸리는 시간에 바람직하지 않은 하한이 설정됩니다.수면 시간은 30초, 2분, 심지어 5분을 고려하세요. tlrbsf 명령은 2초 안에 완료됩니다. 매우 바람직하지 않습니다.둘째, kill 무조건적이므로 이 시퀀스는 실행되지 않는 프로세스를 종료하고 이에 대해 징징거리려고 시도합니다.

그래서...

방법이 있나요? 일반적으로 오래 실행되지만 때로는 빠른 시간 초과("tlrbsf") 명령을 내린다

  • bash 구현이 있습니다 (다른 질문에는 이미 Perl 및 C 답변이 있습니다)
  • 둘 중 빠른 날짜에 종료됩니다. tlrbsf 프로그램 종료 또는 시간 초과
  • 존재하지 않거나 실행되지 않는 프로세스를 종료하지 않습니다(또는 선택적으로:~하지 않을 것이다 불평하다 나쁜 살인에 대해)
  • 1 라이너 일 필요는 없습니다
  • Cygwin 또는 Linux에서 실행 가능

...보너스 포인트의 경우 다음을 실행합니다. tlrbsf 포그라운드의 명령과 백그라운드의 'sleep' 또는 추가 프로세스(예: stdin/stdout/stderr) tlrbsf 명령을 직접 실행한 것처럼 리디렉션할 수 있습니까?

그렇다면 코드를 공유해 주세요.그렇지 않다면 이유를 설명해 주세요.

나는 앞서 언급한 예제를 해킹하려고 한동안 노력했지만 내 bash 기술의 한계에 도달했습니다.

도움이 되었습니까?

해결책

나는 이것이 정확히 당신이 요구하는 것이라고 생각합니다:

http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3

#!/bin/bash
#
# The Bash shell script executes a command with a time-out.
# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
#
# Based on the Bash documentation example.

# Hello Chet,
# please find attached a "little easier"  :-)  to comprehend
# time-out example.  If you find it suitable, feel free to include
# anywhere: the very same logic as in the original examples/scripts, a
# little more transparent implementation to my taste.
#
# Dmitry V Golovashkin <Dmitry.Golovashkin@sas.com>

scriptName="${0##*/}"

declare -i DEFAULT_TIMEOUT=9
declare -i DEFAULT_INTERVAL=1
declare -i DEFAULT_DELAY=1

# Timeout.
declare -i timeout=DEFAULT_TIMEOUT
# Interval between checks if the process is still alive.
declare -i interval=DEFAULT_INTERVAL
# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
declare -i delay=DEFAULT_DELAY

function printUsage() {
    cat <<EOF

Synopsis
    $scriptName [-t timeout] [-i interval] [-d delay] command
    Execute a command with a time-out.
    Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
    signal is blocked, then the subsequent SIGKILL (9) terminates it.

    -t timeout
        Number of seconds to wait for command completion.
        Default value: $DEFAULT_TIMEOUT seconds.

    -i interval
        Interval between checks if the process is still alive.
        Positive integer, default value: $DEFAULT_INTERVAL seconds.

    -d delay
        Delay between posting the SIGTERM signal and destroying the
        process by SIGKILL. Default value: $DEFAULT_DELAY seconds.

As of today, Bash does not support floating point arithmetic (sleep does),
therefore all delay/time values must be integers.
EOF
}

# Options.
while getopts ":t:i:d:" option; do
    case "$option" in
        t) timeout=$OPTARG ;;
        i) interval=$OPTARG ;;
        d) delay=$OPTARG ;;
        *) printUsage; exit 1 ;;
    esac
done
shift $((OPTIND - 1))

# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
    printUsage
    exit 1
fi

# kill -0 pid   Exit code indicates if a signal may be sent to $pid process.
(
    ((t = timeout))

    while ((t > 0)); do
        sleep $interval
        kill -0 $$ || exit 0
        ((t -= interval))
    done

    # Be nice, post SIGTERM first.
    # The 'exit 0' below will be executed if any preceeding command fails.
    kill -s SIGTERM $$ && kill -0 $$ || exit 0
    sleep $delay
    kill -s SIGKILL $$
) 2> /dev/null &

exec "$@"

다른 팁

당신은 아마도 다음을 찾고 있을 것입니다. timeout coreutils의 명령입니다.coreutils의 일부이므로 기술적으로는 C 솔루션이지만 여전히 coreutils입니다. info timeout 상세 사항은.예는 다음과 같습니다.

timeout 5 /path/to/slow/command with options

이 솔루션은 bash 모니터 모드에 관계없이 작동합니다.your_command를 종료하려면 적절한 신호를 사용할 수 있습니다.

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

감시자는 지정된 시간 초과 후 your_command를 종료합니다.스크립트는 느린 작업을 기다리고 감시자를 종료합니다.참고하세요 wait 다른 쉘의 하위 프로세스에서는 작동하지 않습니다.

예:

  • your_command가 2초 이상 실행되어 종료되었습니다.

your_command가 중단되었습니다

( sleep 20 ) & pid=$!
( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi
  • your_command가 시간 초과(20초) 전에 완료되었습니다.

your_command가 완료되었습니다

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

여기요:

timeout --signal=SIGINT 10 /path/to/slow command with options

당신은 변경할 수 있습니다 SIGINT 그리고 10 원하는 대로 ;)

나는 적어도 데비안에 패키지가 있는 "timelimit"를 선호합니다.

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

프로세스를 종료할 때 무언가를 인쇄하고 기본적으로 일정 시간이 지난 후 SIGKILL을 보내기 때문에 coreutils "timeout"보다 조금 더 좋습니다.

당신은 이것을 완전히 할 수 있습니다 bash 4.3 이상:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; r=$?; kill -9 `jobs -p`; exit $r; ) }
  • 예: _timeout 5 longrunning_command args
  • 예: { _timeout 5 producer || echo KABOOM $?; } | consumer
  • 예: producer | { _timeout 5 consumer1; consumer2; }
  • 예: { while date; do sleep .3; done; } | _timeout 5 cat | less

  • Bash 4.3이 필요합니다. wait -n

  • 명령이 종료되면 137을 제공하고, 그렇지 않으면 명령의 반환 값을 제공합니다.
  • 파이프에서 작동합니다.(여기서는 전경으로 갈 필요가 없습니다!)
  • 내부 셸 명령이나 기능에서도 작동합니다.
  • 서브쉘에서 실행되므로 현재 쉘로 변수를 내보낼 수 없습니다. 죄송합니다.

반환 코드가 필요하지 않은 경우 더 간단하게 만들 수 있습니다.

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }

노트:

  • 엄밀히 말하면 필요하지 않습니다. ; ~에 ; ), 그러나 이는 일을 더 일관되게 만듭니다. ; }-사례.그리고 set +b 아마도 남겨둘 수도 있지만 후회하는 것보다는 안전이 더 낫습니다.

  • 제외 --forground (아마도) 모든 변형을 구현할 수 있습니다 timeout 지원합니다. --preserve-status 조금 어렵지만.이것은 독자를 위한 연습으로 남겨둡니다. ;)

이 조리법은 껍질에서 "자연적으로" 사용될 수 있습니다. flock fd):

(
set +b
sleep 20 &
{
YOUR SHELL CODE HERE
} &
wait -n
kill `jobs -p`
)

그러나 위에서 설명한 대로 자연적으로 이러한 방식으로 환경 변수를 둘러싸는 셸로 다시 내보낼 수는 없습니다.

편집하다:

실제 사례:타임아웃 __git_ps1 너무 오래 걸리는 경우(느린 SSHFS-링크 등):

eval "__orig$(declare -f __git_ps1)" && __git_ps1() { ( git() { _timeout 0.3 /usr/bin/git "$@"; }; _timeout 0.3 __orig__git_ps1 "$@"; ) }

편집2:버그픽스.난 그것을 알아 챘다 exit 137 필요하지 않고 만든다 _timeout 동시에 신뢰할 수 없습니다.

편집3: git 완고한 작업이므로 만족스럽게 작동하려면 이중 트릭이 필요합니다.

편집4:잊어버렸어요 _ 처음에는 _timeout 실제 GIT 예를 들어보세요.

또한 참조하십시오 http://www.pixelbeat.org/scripts/timeout 최신 coreutils에 기능이 통합된 스크립트

약간 해키지만 작동합니다.다른 포그라운드 프로세스가 있으면 작동하지 않습니다. (이 문제를 해결하도록 도와주세요!)

sleep TIMEOUT & SPID=${!}; (YOUR COMMAND HERE; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}

실제로 '보너스' 기준을 충족하면 이를 되돌릴 수 있다고 생각합니다.

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa

시간 초과 아마도 시도하는 첫 번째 접근 방식 일 것입니다.시간이 초과된 경우 실행하려면 알림이나 다른 명령이 필요할 수 있습니다.꽤 많이 검색하고 실험한 끝에 이 제품을 생각해냈습니다. 세게 때리다 스크립트:

if 
    timeout 20s COMMAND_YOU_WANT_TO_EXECUTE;
    timeout 20s AS_MANY_COMMANDS_AS_YOU_WANT;
then
    echo 'OK'; #if you want a positive response
else
    echo 'Not OK';
    AND_ALTERNATIVE_COMMANDS
fi

코드 명확성을 갖춘 간단한 스크립트.다음에 저장 /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
$

시간 초과하려면 slowcommand 1초 후:

timeout 1 slowcommand || echo "I failed, perhaps due to time out"

이미 프로그램 이름을 알고 있다면(가정하자 program) 시간 초과 후 종료합니다(예: 3 초), 간단하고 다소 지저분한 대체 솔루션을 제공할 수 있습니다.

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

시스템 호출로 벤치마크 프로세스를 호출하면 완벽하게 작동합니다.

또한 있습니다 cratimeout Martin Cracauer 작성(Unix 및 Linux 시스템용 C로 작성)

# 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'

OS X는 아직 bash 4를 사용하지 않으며 /usr/bin/timeout도 없습니다. 따라서 /usr/bin/timeout과 유사한 home-brew 또는 macport 없이 OS X에서 작동하는 함수는 다음과 같습니다(Tino의 답변).다른 신호에 대한 매개변수 검증, 도움말, 사용법 및 지원은 독자를 위한 연습입니다.

# 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
) }

셸 컨텍스트를 유지하고 시간 초과를 허용하는 문제가 발생했습니다. 유일한 문제는 시간 초과 시 스크립트 실행이 중지된다는 것입니다. 그러나 제시된 요구 사항에는 문제가 없습니다.

#!/usr/bin/env bash

safe_kill()
{
  ps aux | grep -v grep | grep $1 >/dev/null && kill ${2:-} $1
}

my_timeout()
{
  typeset _my_timeout _waiter_pid _return
  _my_timeout=$1
  echo "Timeout($_my_timeout) running: $*"
  shift
  (
    trap "return 0" USR1
    sleep $_my_timeout
    echo "Timeout($_my_timeout) reached for: $*"
    safe_kill $$
  ) &
  _waiter_pid=$!
  "$@" || _return=$?
  safe_kill $_waiter_pid -USR1
  echo "Timeout($_my_timeout) ran: $*"
  return ${_return:-0}
}

my_timeout 3 cd scripts
my_timeout 3 pwd
my_timeout 3 true  && echo true || echo false
my_timeout 3 false && echo true || echo false
my_timeout 3 sleep 10
my_timeout 3 pwd

출력:

Timeout(3) running: 3 cd scripts
Timeout(3) ran: cd scripts
Timeout(3) running: 3 pwd
/home/mpapis/projects/rvm/rvm/scripts
Timeout(3) ran: pwd
Timeout(3) running: 3 true
Timeout(3) ran: true
true
Timeout(3) running: 3 false
Timeout(3) ran: false
false
Timeout(3) running: 3 sleep 10
Timeout(3) reached for: sleep 10
Terminated

물론 나는 dir이라는 디렉토리가 있다고 가정합니다. scripts

#! /bin/bash
timeout=10
interval=1
delay=3
(
    ((t = timeout)) || :

    while ((t > 0)); do
        echo "$t"
        sleep $interval
        # Check if the process still exists.
        kill -0 $$ 2> /dev/null || exit 0
        ((t -= interval)) || :
    done

    # Be nice, post SIGTERM first.
    { echo SIGTERM to $$ ; kill -s TERM $$ ; sleep $delay ; kill -0 $$ 2> /dev/null && { echo SIGKILL to $$ ; kill -s KILL $$ ; } ; }
) &

exec "$@"

내 문제는 아마도 약간 다를 것입니다.원격 시스템에서 ssh를 통해 명령을 시작하고 명령이 중단되면 쉘과 하위 항목을 종료하고 싶습니다.

이제 다음을 사용합니다.

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

이 방법으로 시간 초과가 발생하면 명령은 255를 반환하고 성공할 경우 명령의 반환 코드를 반환합니다.

SSH 세션에서 프로세스 종료는 대화형 셸과 다르게 처리됩니다.그러나 -t 옵션을 ssh에 사용하여 의사 터미널을 할당할 수도 있으므로 대화형 쉘처럼 작동합니다.

다음은 하위 프로세스 생성에 의존하지 않는 버전입니다. 이 기능을 포함하는 독립 실행형 스크립트가 필요했습니다.또한 부분 폴링 간격을 수행하므로 더 빠르게 폴링할 수 있습니다.시간 초과를 선호했지만 이전 서버에 갇혀 있습니다.

# 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

구축 @loup의 답변...

프로세스 시간 초과 및 kill job/pid 출력을 침묵시키려면 다음을 실행하십시오.

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

이렇게 하면 백그라운드 프로세스가 하위 셸에 배치되므로 작업 출력이 표시되지 않습니다.

PHP 스크립트를 호출하는 크론 작업이 있는데 가끔 PHP 스크립트에서 멈추는 경우가 있습니다.이 솔루션은 나에게 완벽했습니다.

나는 사용한다:

scripttimeout -t 60 /script.php

99%의 경우에 대한 대답은 시간 초과 논리를 구현하지 않는 것입니다.시간 초과 논리는 거의 모든 상황에서 빨간색 경고 신호입니다. 또 다른 잘못되었으므로 고쳐야 합니다 대신에.

n초 후에 프로세스가 중단되거나 중단되는 경우가 있습니까?그런 다음 이유를 찾아 대신 수정하세요.

여담으로, strager의 솔루션을 제대로 수행하려면 fg 1 대신 wait "$SPID"를 사용해야 합니다. 왜냐하면 스크립트에는 작업 제어가 없기 때문입니다(그리고 이를 켜려고 하는 것은 어리석은 일입니다).더욱이 fg 1은 스크립트에서 이전에 다른 작업을 시작하지 않았다는 사실에 의존하는데 이는 나쁜 가정입니다.

매우 간단한 방법:

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

~와 함께 pkill (옵션 -에프) 인수를 사용하여 특정 명령을 종료하거나 -n을 지정하여 이전 프로세스 종료를 방지할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top