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

时间:2010-03-02 20:57:37

标签: linux bash cron watchdog

所以我正在尝试将一个cron作业设置为我创建的守护程序的监视程序。如果守护进程出错并失败,我希望cron作业定期重启...我不确定这是多么可能,但我通读了几个cron教程,找不到任何可以做到的东西我我正在寻找......

我的守护进程是从shell脚本启动的,所以我真的只是在寻找一种方法来运行一个cron作业,只要该作业的上一次运行还没有运行。

I found this post,确实提供了我正在尝试使用锁定文件的解决方案,而不是我不确定是否有更好的方法来做...

感谢您的帮助。

16 个答案:

答案 0 :(得分:108)

我为我编写的打印后台处理程序执行此操作,它只是一个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

它每两分钟运行一次,非常有效。如果由于某种原因该过程没有运行,我会通过电子邮件向我发送特殊信息。

答案 1 :(得分:88)

使用flock。这是新的。这样更好。

现在您不必自己编写代码。查看更多原因:https://serverfault.com/a/82863

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

答案 2 :(得分:58)

正如其他人所说,编写和检查PID文件是一个很好的解决方案。这是我的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}"

答案 3 :(得分:22)

不要试图通过cron来做。让cron运行一个脚本,无论如何,然后让脚本决定程序是否正在运行并在必要时启动它(请注意,您可以使用Ruby或Python或您喜欢的脚本语言来执行此操作)

答案 4 :(得分:18)

令人惊讶的是没有人提及run-one。我用这个解决了我的问题。

 apt-get install run-one

然后在crontab脚本

之前添加run-one
*/20 * * * * * run-one python /script/to/run/awesome.py

查看this askubuntu SE答案。您也可以在那里找到详细信息的链接。

答案 5 :(得分:10)

您也可以直接在crontab中以单行方式进行操作:

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

答案 6 :(得分:5)

作为Earlz回答的后续内容,您需要一个包装器脚本,该脚本在启动时创建$ PID.running文件,并在结束时删除。包装器脚本调用您希望运行的脚本。如果目标脚本失败或错误输出,则必须使用包装器,pid文件将被删除..

答案 7 :(得分:4)

我在运行php脚本时的方式是:

crontab:

* * * * * 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)将大于1,因为搜索命令本身包含文件名

所以如果你运行php crons将上面的代码添加到php代码的开头,它只会运行一次。

答案 8 :(得分:3)

我建议使用现有工具,例如monit,它会监控并自动重启进程。有更多可用信息here。它应该在大多数发行版中都很容易获得。

答案 9 :(得分:3)

使用lockrun,您无需为cron作业编写包装脚本。 http://www.unixwiz.net/tools/lockrun.html

答案 10 :(得分:2)

This one从未让我失望过:

<强> 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 job

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

答案 11 :(得分:1)

我建议以下内容作为对rsanden's answer的改进(我发表评论,但没有足够的声誉......):

#!/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的退出状态。

答案 12 :(得分:1)

简单的自定义php足以实现。无需与shell脚本混淆。

假设你想运行 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'

答案 13 :(得分:1)

# 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
}

答案 14 :(得分:0)

如果您要去那条路线,请考虑使用pgrep(如果可用)而不是通过grep传输ps。虽然,就个人而言,我已经从表格

的脚本中获得了很多
while(1){
  call script_that_must_run
  sleep 5
}

虽然这可能会失败并且cron jobs 通常是基本内容的最佳方式。只是另一种选择。

答案 15 :(得分:0)

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

solo是一个非常简单的脚本(10行),可防止程序 一次运行多个副本。用cron做 确保在上一个作业完成之前没有作业。

示例

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