164

我正在尝试将 cron 作业设置为我创建的守护程序的一种看门狗。如果守护程序出错并失败,我希望 cron 作业定期重新启动它......我不确定这有多大可能,但我通读了几个 cron 教程并且找不到任何可以做的事情正在寻找...

我的守护进程是从一个 shell 脚本开始的,所以我真的只是在寻找一种方法来运行一个 cron 作业,前提是该作业的前一次运行尚未运行。

我找到了这篇文章,它确实为我尝试使用锁定文件所做的事情提供了解决方案,但我不确定是否有更好的方法来做到这一点......

4

16 回答 16

170

使用flock. 这是新的。它更好。

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

/usr/bin/flock -n /tmp/my.lockfile /usr/local/bin/my_script
于 2015-10-29T13:51:24.360 回答
124

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

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

于 2010-03-02T21:00:02.470 回答
63

正如其他人所说,编写和检查 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}"
于 2011-09-17T08:04:53.660 回答
46

令人惊讶的是,没有人提到run-one。我已经解决了我的问题。

 apt-get install run-one

然后run-one在您的 crontab 脚本之前添加

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

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

于 2016-08-09T01:00:51.690 回答
25

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

于 2010-03-02T20:59:14.280 回答
10

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

* * * * * [ `ps -ef|grep -v grep|grep <command>` -eq 0 ] && <command>
于 2012-08-14T13:43:00.333 回答
7

当我运行 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 代码的开头,它只会运行一次。

于 2016-08-26T06:51:14.883 回答
6

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

于 2010-03-02T21:01:37.413 回答
4

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

于 2014-10-20T17:36:52.957 回答
3

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

于 2010-03-02T21:02:28.150 回答
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 
于 2017-03-21T13:39:09.237 回答
2

这个从来没有让我失望过:

一个.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 工作

* * * * * /path/to/one.sh <command>
于 2016-10-09T14:02:16.313 回答
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 的退出状态。

于 2014-02-20T21:11:33.087 回答
1

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

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

例子

* * * * * solo -port=3801 ./job.pl blah blah
于 2020-04-16T03:13:31.310 回答
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'
于 2016-04-10T17:42:15.257 回答
0

如果您打算走那条路线,请考虑使用 pgrep (如果可用)而不是 ps 通过 grep 管道。虽然,就个人而言,我已经从表单脚本中获得了很多里程

while(1){
  call script_that_must_run
  sleep 5
}

尽管这可能会失败,但 cron 作业通常是处理重要内容的最佳方式。只是另一种选择。

于 2013-05-05T02:28:12.403 回答