Bash에서 지정된 시간 초과 후 자식 프로세스를 종료하는 방법은 무엇입니까?
나는 때때로 충돌하고 (실제로 멈추는) 자식 프로세스를 시작하는 명백한 이유없이 bash 스크립트를 가지고 있습니다 (닫힌 소스, 그래서 그것에 대해 할 수있는 일은 많지 않습니다). 결과적으로 주어진 시간 동안이 프로세스를 시작하고 주어진 시간 후에 성공적으로 반환되지 않으면 프로세스를 종료하고 싶습니다.
거기에 간단 하고 강력한 것을 사용하여 떠들썩한 파티를 달성하는 방법은?
추신 :이 질문이 serverfault 또는 수퍼 유저에게 더 적합한 지 알려주십시오.
( BASH FAQ 항목 # 68 : "명령을 실행하고 N 초 후에 중단 (시간 초과)하는 방법은 무엇입니까?" )
다운로드가 마음에 들지 않으면 timeout
( sudo apt-get install timeout
)를 사용하고 다음과 같이 사용하십시오 (대부분의 시스템에는 이미 설치되어 있습니다 sudo apt-get install coreutils
)
timeout 10 ping www.goooooogle.com
무언가를 다운로드하지 않으려면 내부에서 시간 초과가 수행하는 작업을 수행하십시오.
( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec ping www.goooooogle.com )
긴 bash 코드에 대해 시간 초과를 수행하려는 경우 다음과 같이 두 번째 옵션을 사용하십시오.
( cmdpid=$BASHPID;
(sleep 10; kill $cmdpid) \
& while ! ping -w 1 www.goooooogle.com
do
echo crap;
done )
# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) &
또는 종료 코드를 얻으려면 :
# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) & waiter=$!
# wait on our worker process and return the exitcode
exitcode=$(wait $pid && echo $?)
# kill the waiter subshell, if it still runs
kill -9 $waiter 2>/dev/null
# 0 if we killed the waiter, cause that means the process finished before the waiter
finished_gracefully=$?
sleep 999&
t=$!
sleep 10
kill $t
나는 또한이 질문을했고 두 가지 더 유용한 것을 발견했습니다.
- bash의 SECONDS 변수
- "pgrep"명령
그래서 나는 커맨드 라인 (OSX 10.9)에서 이와 같은 것을 사용합니다 :
ping www.goooooogle.com & PING_PID=$(pgrep 'ping'); SECONDS=0; while pgrep -q 'ping'; do sleep 0.2; if [ $SECONDS = 10 ]; then kill $PING_PID; fi; done
이것이 루프이므로 CPU를 시원하게 유지하기 위해 "절전 0.2"를 포함했습니다. ;-)
(BTW : 핑은 어쨌든 나쁜 예입니다. 내장 "-t"(시간 초과) 옵션을 사용하면됩니다.)
자식의 pid를 추적하기 위해 pid 파일을 가지고 있거나 쉽게 만들 수 있다고 가정하면 pid 파일의 모드 시간을 확인하고 필요에 따라 프로세스를 종료 / 다시 생성하는 스크립트를 만들 수 있습니다. 그런 다음 스크립트를 crontab에 넣어 대략 필요한 시간에 실행하십시오.
자세한 내용이 필요하면 알려주십시오. 그것이 귀하의 필요에 맞지 않는 것처럼 들리면, 시작은 어떻습니까?
한 가지 방법은 서브 쉘에서 프로그램을 실행하고 read
명령 을 사용하여 명명 된 파이프를 통해 서브 쉘과 통신하는 것 입니다. 이렇게하면 실행중인 프로세스의 종료 상태를 확인하고 파이프를 통해 다시 통신 할 수 있습니다.
다음은 yes
3 초 후에 명령 을 시간 초과하는 예입니다 . 사용하여 프로세스의 PID를 얻습니다 pgrep
(Linux에서만 작동 할 수 있음). 파이프를 사용하는 데에도 문제가 있습니다. 파이프를 읽기 위해 열면 프로세스가 쓰기를 위해 열릴 때까지 멈추거나 그 반대도 마찬가지입니다. 따라서 read
명령이 중단 되는 것을 방지하기 위해 백그라운드 서브 쉘로 읽을 파이프를 "쐐기"했습니다. (동결 파이프 읽기 / 쓰기를 여는 동결을 방지하는 또 다른 방법 read -t 5 <>finished.pipe
은 Linux를 제외하고는 작동하지 않을 수도 있습니다.)
rm -f finished.pipe
mkfifo finished.pipe
{ yes >/dev/null; echo finished >finished.pipe ; } &
SUBSHELL=$!
# Get command PID
while : ; do
PID=$( pgrep -P $SUBSHELL yes )
test "$PID" = "" || break
sleep 1
done
# Open pipe for writing
{ exec 4>finished.pipe ; while : ; do sleep 1000; done } &
read -t 3 FINISHED <finished.pipe
if [ "$FINISHED" = finished ] ; then
echo 'Subprocess finished'
else
echo 'Subprocess timed out'
kill $PID
fi
rm finished.pipe
다음은 프로세스가 이미 종료 된 후 프로세스를 종료하지 않으려는 시도입니다. 동일한 프로세스 ID로 다른 프로세스를 종료 할 가능성이 줄어 듭니다 (이러한 종류의 오류를 완전히 피할 수는 없지만).
run_with_timeout ()
{
t=$1
shift
echo "running \"$*\" with timeout $t"
(
# first, run process in background
(exec sh -c "$*") &
pid=$!
echo $pid
# the timeout shell
(sleep $t ; echo timeout) &
waiter=$!
echo $waiter
# finally, allow process to end naturally
wait $pid
echo $?
) \
| (read pid
read waiter
if test $waiter != timeout ; then
read status
else
status=timeout
fi
# if we timed out, kill the process
if test $status = timeout ; then
kill $pid
exit 99
else
# if the program exited normally, kill the waiting shell
kill $waiter
exit $status
fi
)
}
Use like run_with_timeout 3 sleep 10000
, which runs sleep 10000
but ends it after 3 seconds.
This is like other answers which use a background timeout process to kill the child process after a delay. I think this is almost the same as Dan's extended answer (https://stackoverflow.com/a/5161274/1351983), except the timeout shell will not be killed if it has already ended.
After this program has ended, there will still be a few lingering "sleep" processes running, but they should be harmless.
This may be a better solution than my other answer because it does not use the non-portable shell feature read -t
and does not use pgrep
.
Here's the third answer I've submitted here. This one handles signal interrupts and cleans up background processes when SIGINT
is received. It uses the $BASHPID
and exec
trick used in the top answer to get the PID of a process (in this case $$
in a sh
invocation). It uses a FIFO to communicate with a subshell that is responsible for killing and cleanup. (This is like the pipe in my second answer, but having a named pipe means that the signal handler can write into it too.)
run_with_timeout ()
{
t=$1 ; shift
trap cleanup 2
F=$$.fifo ; rm -f $F ; mkfifo $F
# first, run main process in background
"$@" & pid=$!
# sleeper process to time out
( sh -c "echo \$\$ >$F ; exec sleep $t" ; echo timeout >$F ) &
read sleeper <$F
# control shell. read from fifo.
# final input is "finished". after that
# we clean up. we can get a timeout or a
# signal first.
( exec 0<$F
while : ; do
read input
case $input in
finished)
test $sleeper != 0 && kill $sleeper
rm -f $F
exit 0
;;
timeout)
test $pid != 0 && kill $pid
sleeper=0
;;
signal)
test $pid != 0 && kill $pid
;;
esac
done
) &
# wait for process to end
wait $pid
status=$?
echo finished >$F
return $status
}
cleanup ()
{
echo signal >$$.fifo
}
I've tried to avoid race conditions as far as I can. However, one source of error I couldn't remove is when the process ends near the same time as the timeout. For example, run_with_timeout 2 sleep 2
or run_with_timeout 0 sleep 0
. For me, the latter gives an error:
timeout.sh: line 250: kill: (23248) - No such process
as it is trying to kill a process that has already exited by itself.
'Programing' 카테고리의 다른 글
SVG : rect 안의 텍스트 (0) | 2020.06.03 |
---|---|
왜 못해 (0) | 2020.06.03 |
단일 파일에서 재시작 / 실행 취소 충돌 해결 (0) | 2020.06.03 |
CSS를 사용한 iOS 7의 흐릿한 오버레이 효과? (0) | 2020.06.03 |
반복자를리스트로 변환하는 가장 빠른 방법 (0) | 2020.06.03 |