[bash] 불필요한 지연없이 bash에서 명령 시간 초과

이 답변에 명령 줄 명령 일정한 시간이 지나면 명령을 자동-죽일

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

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

그러나 지정된 “장기 실행”명령이 시간 초과보다 일찍 완료 될 수 있습니다. ( “일반적으로 오래 실행되지만 때로는 빠른”명령 또는 재미를 위해 tlrbsf라고하자 .)

따라서이 멋진 1- 라이너 방식에는 몇 가지 문제가 있습니다. 첫째,는 sleep조건부가 아니므로 시퀀스가 ​​완료되는 데 걸리는 시간에 바람직하지 않은 하한을 설정합니다. tlrbsf 명령이 2 초 안에 완료 될 때 수면을 위해 30 ~ 2m 또는 5m를 고려하십시오 . 둘째, kill는 무조건적이므로이 순서는 실행 중이 아닌 프로세스를 종료시키고 그 과정에서 우회를 시도합니다.

그래서…

일반적으로 오래 실행되지만 가끔 빠른 ( “tlrbsf” ) 명령 을 시간 초과 하는 방법이 있습니까?

  • bash 구현이 있습니다 (다른 질문에는 이미 Perl 및 C 답변이 있습니다)
  • 두 가지의 이전에 종료됩니다 : tlrbsf의 프로그램 종료 또는 제한 시간 경과
  • 존재하지 않거나 실행되지 않는 프로세스를 종료하지 않습니다 (또는 선택적 으로 나쁜 종료에 대해 불평 하지 않습니다 )
  • 1- 라이너 일 필요는 없습니다
  • Cygwin 또는 Linux에서 실행할 수 있습니다

… 그리고 보너스 포인트의 경우, tlrbsf 명령의 stdin / stdout / stderr가 원래의 위치 와 동일하게 리디렉션 될 수 있도록 포 그라운드에서 tlrbsf 명령을 실행하고 백그라운드 에서 ‘sleep’또는 추가 프로세스를 실행합니다. 직접 실행?

그렇다면 코드를 공유하십시오. 그렇지 않은 경우 이유를 설명하십시오.

앞서 언급 한 예제를 해킹하려고 잠시 시간을 보냈지 만 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 "$@"


답변

아마도 timeoutcoreutils 에서 명령을 찾고있을 것입니다 . 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당신이 원하는대로)


답변

이 작업을 전적으로 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`; ) }

노트:

  • 엄밀히 말하면 ;in은 필요하지 않지만 상황에 ; )더 일관성이 ; }있습니다. 그리고 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동시에 신뢰할 수 없다는 것을 알았 습니다.

Edit3 : git다이 하드이므로 만족스럽게 작동하려면 이중 트릭이 필요합니다.

Edit4 : 실제 GIT 예제 _에서 처음으로 a 를 잊었습니다 _timeout.


답변

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

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

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


답변

slowcommand1 초 후 시간 초과하려면

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

자체적 인 이유로 명령이 시간 종료되었는지 또는 실패했는지 판별하려면 상태 코드가 124인지 확인하십시오.

# ping for 3 seconds, but timeout after only 1 second
timeout 1 ping 8.8.8.8 -w3
EXIT_STATUS=$?
if [ $EXIT_STATUS -eq 124 ]
then
echo 'Process Timed Out!'
else
echo 'Process did not timeout. Something else went wrong.'
fi
exit $EXIT_STATUS

종료 상태가 124 인 timeout경우 명령으로 인해 시간 종료되었는지 또는 자체 내부 시간 초과 논리로 인해 명령 자체가 종료 된 후 124로 리턴 되는지 여부를 알 수 없습니다 . 두 경우 모두 안전하게 가정 할 수 있습니다. 그러나 어떤 종류의 시간 초과가 발생했습니다.