cron 작업이 아직 실행되고 있지 않은 경우에만 실행하십시오.


136

그래서 나는 내가 만든 데몬에 대한 일종의 감시자로 cron 작업을 설정하려고합니다. 데몬이 실패하고 실패하면 cron 작업이 주기적으로 다시 시작하기를 원합니다 ...이 방법이 확실하지 않지만 몇 가지 cron 자습서를 읽었으며 내가 할 일을 찾을 수 없었습니다. 찾고 있어요 ...

내 데몬은 쉘 스크립트에서 시작되므로 실제로 해당 작업의 이전 실행이 아직 실행되고 있지 않은 경우에만 cron 작업을 실행하는 방법을 찾고 있습니다.

잠금 파일을 사용하여 수행하려는 작업에 대한 솔루션을 제공하는 이 게시물을 찾았습니다 . 더 나은 방법이 있는지 확실하지 않습니다 ...

당신의 도움을 주셔서 감사합니다.

답변:


120

필자가 작성한 인쇄 스풀러 프로그램을 위해이 작업을 수행합니다. 쉘 스크립트 일뿐입니다.

#!/bin/sh
if ps -ef | grep -v grep | grep doctype.php ; then
        exit 0
else
        /home/user/bin/doctype.php >> /home/user/bin/spooler.log &
        #mailing program
        /home/user/bin/simplemail.php "Print spooler was not running...  Restarted." 
        exit 0
fi

2 분마다 실행되며 매우 효과적입니다. 어떤 이유로 프로세스가 실행되고 있지 않은 경우 특별 정보가 담긴 이메일이 발송됩니다.


4
하지만 매우 안전한 해결책은 아니지만 grep에서 수행 한 검색과 일치하는 다른 프로세스가있는 경우 어떻게해야합니까? rsanden의 대답은 pidfile을 사용하여 이러한 종류의 문제를 방지합니다.
Elias Dorneles

12
이 휠은 이미 다른 곳에서 발명되었다 : 예, serverfault.com/a/82863/108394
필리페 코레

5
grep -v grep | grep doctype.php당신 대신에 할 수 있습니다 grep [d]octype.php.
AlexT

&cron이 스크립트를 실행하는 경우 if 가 필요하지 않습니다 .
lainatnavi

124

사용하다 flock . 이것은 새롭다. 더 좋습니다.

이제 코드를 직접 작성할 필요가 없습니다. https://serverfault.com/a/82863 에서 더 많은 이유를 확인하십시오.

/usr/bin/flock -n /tmp/my.lockfile /usr/local/bin/my_script

1
매우 쉬운 솔루션
MFB

4
최고의 솔루션, 나는 이것을 오랫동안 사용했습니다.
soger

1
나는 또한 멋진 cron 템플릿 요점을 만들었습니다 : gist.github.com/jesslilly/315132a59f749c11b7c6
Jess

3
setlock, s6-setlock, chpst, 그리고 runlock자신의 비 블로킹 모드에서 단지 리눅스보다 더에서 사용할 수있는 대안이다. unix.stackexchange.com/a/475580/5132
JdeBP

3
나는 이것이 받아 들여야 할 답변이라고 생각합니다. 너무 간단합니다!
Codemonkey

62

다른 사람들이 언급했듯이 PID 파일을 작성하고 확인하는 것이 좋습니다. 내 배쉬 구현은 다음과 같습니다.

#!/bin/bash

mkdir -p "$HOME/tmp"
PIDFILE="$HOME/tmp/myprogram.pid"

if [ -e "${PIDFILE}" ] && (ps -u $(whoami) -opid= |
                           grep -P "^\s*$(cat ${PIDFILE})$" &> /dev/null); then
  echo "Already running."
  exit 99
fi

/path/to/myprogram > $HOME/tmp/myprogram.log &

echo $! > "${PIDFILE}"
chmod 644 "${PIDFILE}"

3
+1 pidfile을 사용하면 같은 이름의 프로그램을 실행하는 것보다 훨씬 안전합니다.
Elias Dorneles

/ path / to / myprogram &> $ HOME / tmp / myprogram.log & ?????? 한 당신이 아마 평균 / 경로 /로 / myprogram >> $ HOME / tmp를 / myprogram.log &
마테오

1
스크립트가 끝날 때 파일을 제거해서는 안됩니까? 아니면 내가 아주 명백한 것을 놓치고 있습니까?
Hamzahfrq

1
@ matteo : 네, 그렇습니다. 나는 몇 년 전에 내 노트에서 그것을 고쳤지만 여기서 업데이트하는 것을 잊었다. 더 나쁜 것은, 나는 " >"대 " >>" 만 주목하면서 당신의 의견에서도 그것을 놓쳤다 . 미안합니다.
rsanden

5
@Hamzahfrq : 작동 방식은 다음과 같습니다. 스크립트는 먼저 PID 파일이 있는지 확인합니다 ( " [ -e "${PIDFILE}" ]". 그렇지 않은 경우 백그라운드에서 프로그램을 시작하고 파일에 PID를 씁니다 ( " echo $! > "${PIDFILE}""). PID 파일이 존재하지 않으면 스크립트는 자체 프로세스 ( " ps -u $(whoami) -opid=")를 확인하고 동일한 PID ( " grep -P "^\s*$(cat ${PIDFILE})$"")로 프로세스를 실행 중인지 확인합니다 . 그렇지 않으면 시작합니다. 프로그램을 이전과 같이 PID 파일을 새 PID로 덮어 쓴 다음 종료합니다. 스크립트를 수정할 이유가 없습니다.
rsanden

35

아무도 run-one 에 대해 언급하지 않은 것은 놀랍습니다 . 나는 이것으로 내 문제를 해결했다.

 apt-get install run-one

그런 다음 run-onecrontab 스크립트 앞에 추가 하십시오.

*/20 * * * * * run-one python /script/to/run/awesome.py

askubuntu SE 답변을 확인하십시오 . 자세한 정보에 대한 링크도 찾을 수 있습니다.


22

cron을 통해 시도하지 마십시오. cron이 무엇이든 상관없이 스크립트를 실행 한 다음 스크립트가 프로그램이 실행 중인지 결정하고 필요한 경우 시작하도록하십시오 (Ruby 또는 Python 또는 선호하는 스크립팅 언어를 사용하여이를 수행 할 수 있음)


5
고전적인 방법은 서비스가 시작될 때 생성되는 PID 파일을 읽고 해당 PID가있는 프로세스가 여전히 실행 중인지 확인하고 그렇지 않은 경우 다시 시작하는 것입니다.
tvanfosson

9

crontab에서 직접 하나의 라이너로 수행 할 수도 있습니다.

* * * * * [ `ps -ef|grep -v grep|grep <command>` -eq 0 ] && <command>

5
grep 검색과 일치하는 다른 명령이 있으면 안전하지 않습니까?
Elias Dorneles

1
* * * * * [ ps -ef|grep [c]ommand-eq 0] && <command> 로 쓸 수도 있습니다. 여기서 명령의 첫 글자를 괄호로 묶으면 grep 결과에서 제외됩니다.
Jim Clouse

나는 다음과 같은 문법을 사용해야했다 :[ "$(ps -ef|grep [c]ommand|wc -l)" -eq 0 ] && <command>
thameera

1
이것은 끔찍하다. [ $(grep something | wc -l) -eq 0 ]정말 원형 교차로 작성 ! grep -q something입니다. 따라서 당신은 간단하게 원합니다ps -ef | grep '[c]ommand' || command
tripleee

(또한 따로, 실제로 실제로 일치하는 줄의 수를 grep -c
세려면

7

PHP 스크립트를 실행할 때 내가하는 방식은 다음과 같습니다.

크론 탭 :

* * * * * php /path/to/php/script.php &

PHP 코드 :

<?php
if (shell_exec('ps aux | grep ' . __FILE__ . ' | wc  -l') > 1) {
    exit('already running...');
}
// do stuff

이 명령은 시스템 프로세스 목록에서 현재 PHP 파일 이름을 찾으면 행 카운터 (wc -l)가 하나보다 큽니다.

PHP 크론을 실행하면 PHP 코드의 시작 부분에 위의 코드를 추가하면 한 번만 실행됩니다.


이것은 다른 모든 솔루션이 클라이언트 서버에 무언가를 설치해야했기 때문에 필요한 것입니다. 액세스 할 수 없습니다.
Jeff Davis

5

Earlz의 후속 답변으로 $ PID.running 파일이 시작될 때 작성하고 종료되면 삭제하는 래퍼 스크립트가 필요합니다. 랩퍼 스크립트는 실행하려는 스크립트를 호출합니다. 대상 스크립트가 실패하거나 오류가 발생하면 pid 파일이 삭제되는 경우 래퍼가 필요합니다.


쿨 ... 래퍼 사용에 대해 생각한 적이 없어 ... 데몬에 오류가 발생했을 때 파일이 삭제 될 것이라고 보장 할 수 없었기 때문에 잠금 파일을 사용하여이를 수행하는 방법을 알아낼 수 없었다 ... A 래퍼가 완벽하게 작동합니다. jjclarkson의 솔루션을 제공 할 것입니다. 그래도 작동하지 않으면
이렇게하겠습니다


3

monit 과 같은 기존 도구를 사용하는 것이 좋습니다 . 프로세스를 모니터링하고 자동으로 다시 시작합니다. 자세한 정보는 여기에 있습니다 . 대부분의 배포판에서 쉽게 사용할 수 있어야합니다.


이 답변을 제외한 모든 답변은 "내 cron 작업이 하나의 인스턴스 만 실행하도록하려면 어떻게해야합니까?" 실제 질문은 "다시 시작해도 프로세스를 계속 실행할 수 있습니까?"일 때 올바른 대답은 실제로 cron을 사용하지 않고 monit과 같은 프로세스 관리자입니다. 다른 옵션으로는 runit , s6 또는 배포에서 이미 systemd를 사용하는 경우 살아 있어야하는 프로세스에 대한 시스템 서비스 만 생성하는 것이 있습니다.
clacke

3

이것은 나를 결코 실패시키지 않았다 :

one.sh :

LFILE=/tmp/one-`echo "$@" | md5sum | cut -d\  -f1`.pid
if [ -e ${LFILE} ] && kill -0 `cat ${LFILE}`; then
   exit
fi

trap "rm -f ${LFILE}; exit" INT TERM EXIT
echo $$ > ${LFILE}

$@

rm -f ${LFILE}

크론 직업 :

* * * * * /path/to/one.sh <command>

3
# one instance only (works unless your cmd has 'grep' in it)
ALREADY_RUNNING_EXIT_STATUS=0
bn=`basename $0`
proc=`ps -ef | grep -v grep | grep "$bn" | grep -v " $$ "`
[ $? -eq 0 ] && {
    pid=`echo $proc | awk '{print $2}'`
    echo "$bn already running with pid $pid"
    exit $ALREADY_RUNNING_EXIT_STATUS
}

업데이트 .. 무리를 사용하는 더 좋은 방법 :

/usr/bin/flock -n /tmp/your-app.lock /path/your-app args 

1

rsanden의 답변을 개선하기 위해 다음을 제안합니다 (댓글로 게시했지만 평판이 충분하지 않습니다 ...).

#!/usr/bin/env bash

PIDFILE="$HOME/tmp/myprogram.pid"

if [ -e "${PIDFILE}" ] && (ps -p $(cat ${PIDFILE}) > /dev/null); then
  echo "Already running."
  exit 99
fi

/path/to/myprogram

이것은 잘못된 일치 (및 grepping의 오버 헤드)를 피하고 출력을 억제하고 ps의 종료 상태에만 의존합니다.


1
귀하의 ps명령은 사용자 자신뿐만 아니라 시스템의 다른 사용자에 대한 PID와 일치합니다. 명령에 " -u"를 추가 ps하면 종료 상태 작동 방식이 변경됩니다.
rsanden

1

간단한 커스텀 PHP로 충분합니다. 쉘 스크립트와 혼동 할 필요가 없습니다.

실행 하지 않는 경우 php /home/mypath/example.php 를 실행한다고 가정합니다.

그런 다음 다음 사용자 정의 PHP 스크립트를 사용하여 동일한 작업을 수행하십시오.

다음 /home/mypath/forever.php를 만듭니다.

<?php
    $cmd = $argv[1];
    $grep = "ps -ef | grep '".$cmd."'";
    exec($grep,$out);
    if(count($out)<5){
        $cmd .= ' > /dev/null 2>/dev/null &';
        exec($cmd,$out);
        print_r($out);
    }
?>

그런 다음 cron에서 다음을 추가하십시오.

* * * * * php /home/mypath/forever.php 'php /home/mypath/example.php'

0

해당 경로로 이동하려는 경우 grep을 통해 파이프 된 ps 대신 pgrep (사용 가능한 경우)을 사용하는 것이 좋습니다. 개인적으로 양식의 스크립트에서 많은 마일리지를 얻었지만

while(1){
  call script_that_must_run
  sleep 5
}

이것이 실패 할 수는 있지만 cron 작업 종종 필수 항목에 가장 적합한 방법입니다. 또 다른 대안.


2
이것은 단지 데몬을 계속해서 시작하고 위에서 언급 한 문제를 해결하지 못합니다.
cwoebker

0

문서 : https://www.timkay.com/solo/

solo는 프로그램이 한 번에 둘 이상의 사본을 실행하지 못하게하는 매우 간단한 스크립트 (10 줄)입니다. cron에서는 이전 작업이 완료되기 전에 작업이 실행되지 않도록하는 것이 유용합니다.

* * * * * solo -port=3801 ./job.pl blah blah
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.