Jalankan pekerjaan cron hanya jika belum berjalan

136

Jadi saya mencoba mengatur pekerjaan cron sebagai semacam pengawas untuk daemon yang saya buat. Jika daemon error keluar dan gagal, saya ingin tugas cron me-restart secara berkala ... Saya tidak yakin bagaimana ini mungkin, tapi saya membaca beberapa tutorial cron dan tidak dapat menemukan apa pun yang akan melakukan apa yang saya lakukan. sedang mencari ...

Daemon saya dimulai dari skrip shell, jadi saya benar-benar hanya mencari cara untuk menjalankan pekerjaan cron SAJA jika menjalankan pekerjaan sebelumnya tidak berjalan.

Saya menemukan posting ini , yang memang memberikan solusi untuk apa yang saya coba lakukan dengan menggunakan file kunci, bukan saya tidak yakin apakah ada cara yang lebih baik untuk melakukannya ...

Terima kasih atas bantuan Anda.

LorenVS
sumber

Jawaban:

120

Saya melakukan ini untuk program spooler cetak yang saya tulis, itu hanya skrip shell:

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

Ini berjalan setiap dua menit dan cukup efektif. Saya memilikinya mengirimi saya email dengan informasi khusus jika karena alasan tertentu prosesnya tidak berjalan.

jjclarkson
sumber
4
bukan solusi yang sangat aman, bagaimana jika ada proses lain yang cocok dengan pencarian yang Anda lakukan di grep? Jawaban rsanden mencegah masalah semacam itu menggunakan pidfile.
Elias Dorneles
12
Roda ini sudah ditemukan di tempat lain :) Misalnya, serverfault.com/a/82863/108394
Filipe Correia
5
Alih-alih grep -v grep | grep doctype.phpbisa Anda lakukan grep [d]octype.php.
AlexT
Perhatikan bahwa tidak perlu untuk &itu jika cron menjalankan skrip.
lainatnavi
124

Gunakan flock. Baru. Lebih baik.

Sekarang Anda tidak perlu menulis kode sendiri. Lihat lebih banyak alasan di sini: https://serverfault.com/a/82863

/usr/bin/flock -n /tmp/my.lockfile /usr/local/bin/my_script
Jess
sumber
1
Solusi yang sangat mudah
MFB
4
Solusi terbaik, saya sudah menggunakan ini untuk waktu yang sangat lama.
soger
1
Saya juga membuat inti template cron yang bagus di sini: gist.github.com/jesslilly/315132a59f749c11b7c6
Jess
3
setlock, s6-setlock, chpst, Dan runlockdi mereka mode non-blocking alternatif yang tersedia di lebih dari sekedar Linux. unix.stackexchange.com/a/475580/5132
JdeBP
3
Saya merasa ini harus menjadi jawaban yang diterima. Sangat sederhana!
Codemonkey
62

Seperti yang dinyatakan orang lain, menulis dan memeriksa file PID adalah solusi yang baik. Inilah implementasi bash saya:

#!/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}"
Randen
sumber
3
+1 Menggunakan pidfile mungkin jauh lebih aman daripada menangkap program yang sedang berjalan dengan nama yang sama.
Elias Dorneles
/ path / ke / myprogram &> $ HOME / tmp / myprogram.log & ?????? mungkin maksud Anda / path / ke / myprogram >> $ HOME / tmp / myprogram.log &
matteo
1
Bukankah seharusnya file dihapus ketika skrip selesai? Atau apakah saya melewatkan sesuatu yang sangat jelas?
Hamzahfrq
1
@matteo: Ya, Anda benar. Saya telah memperbaikinya dalam catatan saya bertahun-tahun yang lalu tetapi lupa untuk memperbaruinya di sini. Lebih buruk lagi, saya ketinggalan dalam komentar Anda juga, hanya memperhatikan " >" versus " >>". Maaf soal itu.
rsanden
5
@ Hamzahfrq: Begini cara kerjanya: Skrip pertama-tama memeriksa apakah file PID ada (" [ -e "${PIDFILE}" ]". Jika tidak, maka ia akan memulai program di latar belakang, tulis PID-nya ke file (" echo $! > "${PIDFILE}""), dan Jika file PID bukan ada, maka skrip akan memeriksa proses Anda sendiri (" ps -u $(whoami) -opid=") dan melihat apakah Anda menjalankannya dengan PID yang sama (" grep -P "^\s*$(cat ${PIDFILE})$""). Jika tidak, maka file akan memulai program seperti sebelumnya, menimpa file PID dengan PID baru, dan keluar. Saya tidak melihat alasan untuk memodifikasi skrip; kan?
rsanden
35

Mengejutkan bahwa tidak ada yang menyebutkan tentang run-one . Saya telah memecahkan masalah saya dengan ini.

 apt-get install run-one

lalu tambahkan run-onesebelum skrip crontab Anda

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

Periksa ini askubuntu SE jawaban. Anda dapat menemukan tautan ke informasi terperinci di sana juga.

Bedi Egilmez
sumber
22

Jangan mencoba melakukannya melalui cron. Mintalah cron menjalankan skrip apa pun, dan minta skrip memutuskan apakah program sedang berjalan dan jalankan jika perlu (perhatikan Anda dapat menggunakan Ruby atau Python atau bahasa skrip favorit Anda untuk melakukan ini)

Earlz
sumber
5
Cara klasik adalah dengan membaca file PID yang dibuat oleh layanan ketika dimulai, periksa apakah proses dengan PID itu masih berjalan, dan restart jika tidak.
tvanfosson
9

Anda juga dapat melakukannya sebagai one-liner langsung di crontab Anda:

* * * * * [ `ps -ef|grep -v grep|grep <command>` -eq 0 ] && <command>
quezacoatl
sumber
5
tidak terlalu aman, bagaimana jika ada perintah lain yang cocok dengan pencarian grep?
Elias Dorneles
1
Ini juga dapat ditulis sebagai * * * * * [ ps -ef|grep [c]ommand-eq 0] && <command> di mana membungkus huruf pertama dari perintah Anda dalam tanda kurung mengecualikannya dari hasil grep.
Jim Clouse
Saya harus menggunakan sintaks berikut:[ "$(ps -ef|grep [c]ommand|wc -l)" -eq 0 ] && <command>
thameera
1
Ini mengerikan. [ $(grep something | wc -l) -eq 0 ]adalah cara yang sangat bundaran untuk menulis ! grep -q something. Jadi Anda hanya inginps -ef | grep '[c]ommand' || command
tripleee
(Juga, sebagai tambahan, jika Anda benar-benar ingin menghitung jumlah garis yang cocok, itu grep -c.)
tripleee
7

Cara saya melakukannya ketika saya menjalankan skrip php adalah:

Crontab:

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

Kode php:

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

Perintah ini mencari dalam daftar proses sistem untuk nama file php saat ini jika ada penghitung baris (wc -l) akan lebih besar dari satu karena perintah pencarian itu sendiri berisi nama file php

jadi jika Anda menjalankan php crons tambahkan kode di atas ke awal kode php Anda dan itu akan berjalan hanya sekali.

taliboni
sumber
Inilah yang saya butuhkan karena semua solusi lain diperlukan menginstal sesuatu di server klien, yang tidak dapat saya akses.
Jeff Davis
5

Sebagai tindak lanjut dari jawaban Earlz, Anda memerlukan skrip wrapper yang membuat file $ PID.running ketika dimulai, dan menghapus ketika itu berakhir. Skrip wrapper memanggil skrip yang ingin Anda jalankan. Pembungkus diperlukan jika skrip target gagal atau kesalahan keluar, file pid akan dihapus ..

Byron Whitlock
sumber
Oh, keren ... Saya tidak pernah berpikir tentang menggunakan pembungkus ... Saya tidak bisa menemukan cara untuk melakukannya menggunakan file kunci karena saya tidak dapat menjamin bahwa file tersebut akan dihapus jika daemonnya salah ... A wrapper akan bekerja dengan sempurna, saya akan memberikan solusi jjclarkson, tetapi saya akan melakukan ini jika itu tidak berhasil ...
LorenVS
3

Saya akan merekomendasikan untuk menggunakan alat yang ada seperti monit , itu akan memonitor dan proses restart otomatis. Ada informasi lebih lanjut tersedia di sini . Itu harus mudah tersedia di sebagian besar distribusi.

Zitrax
sumber
Setiap jawaban kecuali jawaban ini menjawab pertanyaan permukaan, "Bagaimana pekerjaan cron saya memastikan hanya menjalankan satu instance?" ketika pertanyaan sebenarnya adalah "Bagaimana saya bisa menjaga proses saya tetap berjalan di hadapan restart?", dan jawaban yang benar adalah tidak menggunakan cron, tetapi sebagai pengawas proses seperti monit. Opsi lain termasuk runit , s6 atau, jika distribusi Anda sudah menggunakan systemd, hanya membuat layanan systemd untuk proses yang perlu dijaga tetap hidup.
clacke
3

Yang ini tidak pernah mengecewakan saya:

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}

pekerjaan cron :

* * * * * /path/to/one.sh <command>
DJV
sumber
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
}

UPDATE .. cara yang lebih baik menggunakan kawanan:

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

Saya menyarankan yang berikut ini sebagai perbaikan untuk jawaban rsanden (saya akan memposting sebagai komentar, tetapi tidak memiliki reputasi yang cukup ...):

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

Ini menghindari kemungkinan kecocokan salah (dan overhead dari grepping), dan itu menekan output dan hanya bergantung pada status keluar ps.

dbenton
sumber
1
psPerintah Anda akan cocok dengan PID untuk pengguna lain di sistem, bukan hanya milik Anda. Menambahkan " -u" ke psperintah mengubah cara status keluar bekerja.
rsanden
1

Php kustom sederhana sudah cukup untuk mencapai. Tidak perlu bingung dengan skrip shell.

mari kita asumsikan Anda ingin menjalankan php /home/mypath/example.php jika tidak berjalan

Kemudian gunakan skrip php khusus berikut untuk melakukan pekerjaan yang sama.

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

Kemudian di cron Anda tambahkan berikut ini

* * * * * php /home/mypath/forever.php 'php /home/mypath/example.php'
Lingeshram
sumber
0

Pertimbangkan untuk menggunakan pgrep (jika tersedia) daripada ps yang disalurkan melalui grep jika Anda akan menempuh rute itu. Padahal, secara pribadi, saya punya banyak jarak tempuh dari skrip formulir

while(1){
  call script_that_must_run
  sleep 5
}

Meskipun ini bisa gagal dan pekerjaan cron adalah sering cara terbaik untuk hal-hal penting. Alternatif lain.

Richard Thomas
sumber
2
Ini hanya akan memulai daemon berulang-ulang dan tidak memecahkan masalah yang disebutkan di atas.
cwoebker
0

Documents: https://www.timkay.com/solo/

solo adalah skrip yang sangat sederhana (10 baris) yang mencegah suatu program menjalankan lebih dari satu salinan sekaligus. Berguna dengan cron untuk memastikan bahwa suatu pekerjaan tidak berjalan sebelum yang sebelumnya selesai.

Contoh

* * * * * solo -port=3801 ./job.pl blah blah
Erlang P
sumber