仅在 cron 作业尚未运行时运行它

问题描述 投票:0回答:16

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

我的守护进程是从 shell 脚本启动的,所以我实际上只是在寻找一种方法来运行 cron 作业,前提是该作业的上一次运行尚未仍在运行。

我找到了这篇文章,它确实为我尝试使用锁定文件执行的操作提供了解决方案,而不是我不确定是否有更好的方法......

linux bash cron watchdog
16个回答
184
投票

使用

flock
。这是新的。好多了。

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

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

129
投票

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

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


65
投票

正如其他人所说,写入和检查 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}"

58
投票

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

 apt-get install run-one

然后在 crontab 脚本之前添加

run-one

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

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


28
投票

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


9
投票

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

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

9
投票

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


6
投票

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


5
投票

使用

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


4
投票
# 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 

3
投票

我建议使用现有的工具,例如 monit,它将监视并自动重新启动进程。 此处提供更多信息。它应该可以在大多数发行版中轻松使用。


2
投票

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

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 作业:

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

2
投票

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

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

示例

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

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

这避免了可能的错误匹配(以及 grep 的开销),并且它抑制输出并仅依赖于 ps 的退出状态。


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'

0
投票

如果您打算走这条路,请考虑使用 pgrep(如果可用)而不是通过 grep 进行 ps 管道传输。不过,就我个人而言,我已经从这种形式的脚本中获得了很多经验

while(1){
  call script_that_must_run
  sleep 5
}

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

© www.soinside.com 2019 - 2024. All rights reserved.