Cron işini yalnızca zaten çalışmıyorsa çalıştırın


136

Bu yüzden, oluşturduğum bir arka plan programı için bir tür bekçi köpeği olarak bir cron işi kurmaya çalışıyorum. Arka plan programı hata verirse ve başarısız olursa, cron işinin onu periyodik olarak yeniden başlatmasını istiyorum ... Bunun ne kadar mümkün olduğundan emin değilim, ancak birkaç cron öğreticisini okudum ve yaptığım şeyi yapacak hiçbir şey bulamadım arıyorum ...

Arka plan programım bir kabuk betiğinden başlatılıyor, bu yüzden YALNIZCA o işin önceki çalıştırması hala çalışmıyorsa bir cron işini çalıştırmanın bir yolunu arıyorum.

Kilit dosyalarını kullanarak yapmaya çalıştığım şey için bir çözüm sağlayan bu yazıyı buldum , bunu yapmanın daha iyi bir yolu olup olmadığından emin değilim ...

Yardımınız için teşekkürler.

Yanıtlar:


120

Bunu yazdığım bir yazdırma biriktirici programı için yapıyorum, bu sadece bir kabuk betiği:

#!/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

Her iki dakikada bir çalışır ve oldukça etkilidir. Herhangi bir nedenle işlem çalışmıyorsa bana özel bilgilerle e-posta gönderdim.


4
çok güvenli bir çözüm değil, yine de grep'te yaptığınız aramayla eşleşen başka bir süreç varsa? rsanden'in cevabı, bir pidfile kullanarak bu tür sorunları önler.
Elias Dorneles

12
Bu tekerlek zaten başka bir yerde icat edildi :) Ör. Serverfault.com/a/82863/108394
Filipe Correia

5
grep -v grep | grep doctype.phpSenin yerine yapabilirsin grep [d]octype.php.
AlexT

&Komut dosyasını çalıştıran cron ise buna gerek olmadığını unutmayın .
lainatnavi

125

Kullanın flock. Bu yeni. Daha iyi.

Artık kodu kendiniz yazmanıza gerek yok. Daha fazla nedene buradan göz atın: https://serverfault.com/a/82863

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

1
Çok kolay bir çözüm
MFB

4
En iyi çözüm, bunu gerçekten uzun süredir kullanıyorum.
2016

1
Ayrıca burada güzel bir cron şablonu özeti oluşturdum: gist.github.com/jesslilly/315132a59f749c11b7c6
Jess

3
setlock, s6-setlock, chpstVe runlockonların engellenmeyen modlarda sadece Linux daha mevcuttur alternatiflerdir. unix.stackexchange.com/a/475580/5132
JdeBP

3
Bunun kabul edilen cevap olması gerektiğini düşünüyorum. Çok basit!
Codemonkey

62

Diğerlerinin de belirttiği gibi, bir PID dosyası yazmak ve kontrol etmek iyi bir çözümdür. İşte benim bash uygulamam:

#!/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 Bir pidfile kullanmak, muhtemelen aynı ada sahip çalışan bir program için grep etmekten çok daha güvenlidir.
Elias Dorneles

/ yol / programıma &> $ HOME / tmp / myprogram.log & ?????? belki / myprogram / yol / yolu mu demek istediniz >> $ HOME / tmp / myprogram.log &
matteo

1
Komut dosyası bittiğinde dosyanın kaldırılması gerekmez mi? Yoksa çok bariz bir şeyi mi kaçırıyorum?
Hamzahfrq

1
@matteo: Evet, haklısın. Bunu yıllar önce notlarımda düzelttim ama burada güncellemeyi unuttum. Daha da kötüsü, yorumunuzda da kaçırdım, sadece " >" ve " >>" farkına vardım . Bunun için üzgünüm.
rsanden

5
@Hamzahfrq: Şu şekilde çalışır: Komut dosyası önce PID dosyasının var olup olmadığını kontrol eder (" [ -e "${PIDFILE}" ]". Yoksa, programı arka planda başlatır, PID'sini bir dosyaya (" echo $! > "${PIDFILE}"") yazar ve Çık. Bunun yerine PID dosyası mevcutsa, komut dosyası kendi işlemlerinizi (" ps -u $(whoami) -opid=") kontrol edecek ve aynı PID'ye (" grep -P "^\s*$(cat ${PIDFILE})$"") sahip birini çalıştırıp çalıştırmadığınızı kontrol edecektir . Eğer değilseniz, o zaman başlayacaktır. programı daha önce olduğu gibi, yeni PID ile PID dosyasının üzerine yazın ve çıkın. Komut dosyasını değiştirmek için bir neden göremiyorum; siz misiniz?
rsanden

35

Kimsenin Run- One'dan bahsetmemesi şaşırtıcı . Sorunumu bununla çözdüm.

 apt-get install run-one

sonra run-onecrontab betiğinizden önce ekleyin

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

Bu askubuntu SE cevabına göz atın. Burada ayrıntılı bir bilgi için bağlantı da bulabilirsiniz.


22

Bunu cron ile yapmaya çalışmayın. Cron'un ne olursa olsun bir komut dosyası çalıştırmasını sağlayın ve ardından betiğin programın çalışıp çalışmadığına karar vermesini ve gerekirse başlatmasını sağlayın (bunu yapmak için Ruby veya Python'u veya en sevdiğiniz komut dosyası dilini kullanabileceğinizi unutmayın)


5
Klasik yol, hizmetin başladığında oluşturduğu bir PID dosyasını okumak, bu PID ile işlemin hala çalışıp çalışmadığını kontrol etmek ve çalışmıyorsa yeniden başlatmaktır.
tvanfosson

9

Bunu doğrudan crontab'inizde tek satırlık olarak da yapabilirsiniz:

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

5
çok güvenli değil, ya grep aramasıyla eşleşen başka komutlar varsa?
Elias Dorneles

1
Bu, * * * * * [ ps -ef|grep [c]ommand-eq 0] && <command> şeklinde de yazılabilir, burada komutunuzun ilk harfini parantez içine almak grep sonuçlarının dışında bırakır.
Jim Clouse

Aşağıdaki sözdizimini kullanmak zorunda kaldım:[ "$(ps -ef|grep [c]ommand|wc -l)" -eq 0 ] && <command>
thameera

1
Bu çok çirkin. [ $(grep something | wc -l) -eq 0 ]gerçekten dolambaçlı bir yazma yoludur ! grep -q something. Yani basitçe istiyorsunps -ef | grep '[c]ommand' || command
üçlü

(Ayrıca, bir kenara olarak, gerçekten eşleşen satırların sayısını gerçekten saymak istiyorsanız, bu grep -c.)
2016

7

PHP betikleri çalıştırırken bunu yapma şeklim şudur:

Crontab:

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

Php kodu:

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

Bu komut, sistem işlem listesinde mevcut php dosya adını arıyor, eğer varsa satır sayacı (wc -l) birden büyük olacaktır çünkü arama komutunun kendisi dosya adını içerir

bu yüzden php crons çalıştırıyorsanız yukarıdaki kodu php kodunuzun başlangıcına ekleyin ve yalnızca bir kez çalışacaktır.


Diğer tüm çözümler istemci sunucusuna bir şey yüklemeyi gerektirdiğinden ihtiyacım olan şey buydu ve benim erişimim yok.
Jeff Davis

5

Earlz cevabının devamı olarak, başladığında bir $ PID.running dosyası oluşturan ve bittiğinde silen bir sarmalayıcı betiğine ihtiyacınız vardır. Sarmalayıcı komut dosyası çalıştırmak istediğiniz betiği çağırır. Sarmalayıcı, hedef komut dosyasının başarısız olması veya hata yapması durumunda gereklidir, pid dosyası silinir ..


Harika ... Bir sarmalayıcı kullanmayı hiç düşünmemiştim ... Kilit dosyalarını kullanarak bunu yapmanın bir yolunu bulamadım çünkü arka plan programı hata yaptığında dosyanın silineceğini garanti edemedim ... A sarıcı mükemmel çalışırdı, jjclarkson'ın çözümüne bir şans vereceğim, ama işe yaramazsa bunu yapacağım ...
LorenVS


3

Monit gibi mevcut bir aracı kullanmanızı tavsiye ederim , süreçleri izleyecek ve otomatik olarak yeniden başlatacaktır. Burada daha fazla bilgi var . Çoğu dağıtımda kolayca bulunabilmelidir.


Bunun dışındaki her yanıt, yüzey sorusuna yanıt verir: "Cron işim yalnızca bir örnek çalıştırdığından nasıl emin olabilir?" asıl soru "Sürecimi yeniden başlatmalar karşısında nasıl çalışır durumda tutabilirim?" olduğunda ve doğru cevap gerçekten cron kullanmak yerine monit gibi bir süreç süpervizörü kullanmaktır. Diğer seçenekler arasında runit , s6 veya dağıtımınız zaten systemd kullanıyorsa, sadece canlı tutulması gereken süreç için bir systemd hizmeti oluşturmayı içerir.
clacke

3

Bu beni asla hayal kırıklığına uğratmadı:

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}

cron işi :

* * * * * /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
}

GÜNCELLEME .. sürü kullanmanın daha iyi yolu:

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

1

Aşağıdakileri rsanden'in cevabına bir iyileştirme olarak öneririm (yorum olarak gönderirdim, ancak yeterli itibarım yok ...):

#!/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

Bu, olası yanlış eşleşmeleri (ve grepleme ek yükünü) önler ve çıktıyı bastırır ve yalnızca ps'nin çıkış durumuna dayanır.


1
Sizin pskomut sadece kendi değil, sistemdeki diğer kullanıcılar için PID'leri eşleşir. Komuta bir " -u" eklemek ps, çıkış durumunun çalışma şeklini değiştirir.
rsanden

1

Basit özel php elde etmek için yeterlidir. Kabuk betiğiyle karıştırmaya gerek yok.

Çalıştırmak istediğiniz varsayalım sağlar php /home/mypath/example.php çalışmıyorsa eğer

Ardından, aynı işi yapmak için aşağıdaki özel php komut dosyasını kullanın.

/home/mypath/forever.php'yi takip ederek oluştur

<?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);
    }
?>

Sonra cron'unuzda aşağıdakileri ekleyin

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

0

Bu rotaya gidecekseniz, grep aracılığıyla gönderilen ps yerine pgrep (varsa) kullanmayı düşünün. Yine de şahsen, formun senaryolarından çok fazla yol aldım

while(1){
  call script_that_must_run
  sleep 5
}

Bu başarısız edebilir rağmen cron işleri vardır genellikle temel şeyler için en iyi yol. Sadece başka bir alternatif.


2
Bu sadece arka plan programını tekrar tekrar başlatır ve yukarıda bahsedilen sorunu çözmez.
cwoebker

0

Dokümanlar: https://www.timkay.com/solo/

solo, bir programın aynı anda birden fazla kopya çalıştırmasını engelleyen çok basit bir betiktir (10 satır). Bir işin önceki bir iş bitmeden çalışmadığından emin olmak cron ile kullanışlıdır.

Misal

* * * * * solo -port=3801 ./job.pl blah blah
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.