불필요한 지연 없이 bash에서 명령 시간 초과
-
22-08-2019 - |
문제
이 답변 에게 일정 시간이 지나면 명령을 자동 종료하는 명령줄 명령
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을 지정하여 이전 프로세스 종료를 방지할 수 있습니다.