简体   繁体   English

仅当 cron 作业尚未运行时才运行它

[英]Run cron job only if it isn't already running

I'm trying to set up a cron job as a sort of watchdog for a daemon that I've created.我正在尝试将 cron 作业设置为我创建的守护进程的一种看门狗。 If the daemon errors out and fails, I want the cron job to periodically restart it... I'm not sure how possible this is, but I read through a couple of cron tutorials and couldn't find anything that would do what I'm looking for...如果守护程序出错并失败,我希望 cron 作业定期重新启动它......我不确定这有多大可能,但我阅读了几个 cron 教程,但找不到任何可以做我想做的事情我在找...

My daemon gets started from a shell script, so I'm really just looking for a way to run a cron job ONLY if the previous run of that job isn't still running.我的守护进程是从 shell 脚本启动的,所以我真的只是在寻找一种方法来运行 cron 作业,前提是该作业的上一次运行尚未运行。

I found this post , which did provide a solution for what I'm trying to do using lock files, not I'm not sure if there is a better way to do it...我找到了这篇文章,它确实为我尝试使用锁定文件做的事情提供了解决方案,但我不确定是否有更好的方法来做到这一点......

Use flock .使用flock It's new.这是新的。 It's better.更好。

Now you don't have to write the code yourself.现在您不必自己编写代码。 Check out more reasons here: https://serverfault.com/a/82863在此处查看更多原因: https : //serverfault.com/a/82863

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

I do this for a print spooler program that I wrote, it's just a shell script:我为我编写的打印假脱机程序执行此操作,它只是一个 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

It runs every two minutes and is quite effective.它每两分钟运行一次,非常有效。 I have it email me with special information if for some reason the process is not running.如果由于某种原因进程没有运行,我会通过电子邮件向我发送特殊信息。

As others have stated, writing and checking a PID file is a good solution.正如其他人所说,编写和检查 PID 文件是一个很好的解决方案。 Here's my bash implementation:这是我的 bash 实现:

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

It's suprising that no one mentioned about run-one .令人惊讶的是没有人提到run-one I've solved my problem with this.我已经解决了我的问题。

 apt-get install run-one

then add run-one before your crontab script然后在 crontab 脚本之前添加run-one

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

Check out this askubuntu SE answer.看看这个askubuntu SE 答案。 You can find link to a detailed information there as well.您也可以在那里找到详细信息的链接。

Don't try to do it via cron.不要尝试通过 cron 来实现。 Have cron run a script no matter what, and then have the script decide if the program is running and start it if necessary (note you can use Ruby or Python or your favorite scripting language to do this)无论如何,让 cron 运行一个脚本,然后让脚本决定程序是否正在运行并在必要时启动它(请注意,您可以使用 Ruby 或 Python 或您喜欢的脚本语言来执行此操作)

您也可以直接在 crontab 中将其作为单行:

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

The way I am doing it when I am running php scripts is:我在运行 php 脚本时这样做的方式是:

The crontab:定时任务:

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

The php code: php代码:

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

This command is searching in the system process list for the current php filename if it exists the line counter (wc -l) will be greater then one because the search command itself containing the filename此命令正在系统进程列表中搜索当前 php 文件名,如果它存在,行计数器 (wc -l) 将大于 1,因为搜索命令本身包含文件名

so if you running php crons add the above code to the start of your php code and it will run only once.所以如果你运行 php crons 将上面的代码添加到你的 php 代码的开头,它只会运行一次。

As a follow up to Earlz answer, you need a wrapper script that creates a $PID.running file when it starts, and delete when it ends.作为 Earlz 回答的后续,您需要一个包装脚本,该脚本在开始时创建 $PID.running 文件,并在结束时删除。 The wrapper script calls the script you wish to run.包装器脚本调用您希望运行的脚本。 The wrapper is necessary in case the target script fails or errors out, the pid file gets deleted..包装器是必要的,以防目标脚本失败或出错,pid 文件被删除..

With lockrun you don't need to write a wrapper script for your cron job.使用lockrun您无需为您的 cron 作业编写包装脚本。 http://www.unixwiz.net/tools/lockrun.html http://www.unixwiz.net/tools/lockrun.html

I would recommend to use an existing tool such as monit , it will monitor and auto restart processes.我建议使用现有的工具,例如monit ,它将监视和自动重启进程。 There is more information available here . 此处提供更多信息。 It should be easily available in most distributions.它应该在大多数发行版中很容易获得。

# 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 .. better way using flock:更新 .. 使用 flock 的更好方法:

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

This one never failed me:这个从来没有让我失望:

one.sh :一个.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 job :定时任务

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

I'd suggest the following as an improvement to rsanden's answer (I'd post as a comment, but don't have enough reputation...):我建议将以下内容作为对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

This avoids possible false matches (and the overhead of grepping), and it suppresses output and relies only on exit status of ps.这避免了可能的错误匹配(以及 grepping 的开销),并且它会抑制输出并且仅依赖于 ps 的退出状态。

Simple custom php is enough to achieve.简单的自定义php就足以实现。 No need to confuse with shell script.无需与 shell 脚本混淆。

lets assume you want to run php /home/mypath/example.php if not running假设你想运行php /home/mypath/example.php如果没有运行

Then use following custom php script to do the same job.然后使用以下自定义 php 脚本来做同样的工作。

create following /home/mypath/forever.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);
    }
?>

Then in your cron add following然后在您的 cron 中添加以下内容

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

Docs: https://www.timkay.com/solo/文档: https : //www.timkay.com/solo/

solo is a very simple script (10 lines) that prevents a program from running more than one copy at a time. solo 是一个非常简单的脚本(10 行),它可以防止程序一次运行多个副本。 It is useful with cron to make sure that a job doesn't run before a previous one has finished.使用 cron 可以确保作业在前一个作业完成之前不会运行。

Example例子

* * * * * solo -port=3801 ./job.pl blah blah

Consider using pgrep (if available) rather than ps piped through grep if you're going to go that route.如果您打算走那条路,请考虑使用 pgrep(如果可用)而不是通过 grep 管道传输的 ps。 Though, personally, I've got a lot of mileage out of scripts of the form虽然,就个人而言,我从表单的脚本中获得了很多好处

while(1){
  call script_that_must_run
  sleep 5
}

Though this can fail and cron jobs are often the best way for essential stuff.虽然这可能会失败,并cron作业往往必不可少的东西的最佳方式。 Just another alternative.只是另一种选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM