【问题标题】:Run shell script exactly once with cronjob使用 cronjob 只运行一次 shell 脚本
【发布时间】:2016-06-24 06:33:22
【问题描述】:

我有一个 cron 作业,它调用一个每小时检查一次更新的脚本。如果有更新,它将调用update.sh,而后者又调用commands.sh。我希望每个“新”commands.sh 只运行一次。

每次我使用该文件的某个唯一 ID 运行 commands.sh 时,我都在考虑写入文件。在运行commands.sh 之前,我检查文件是否包含commands.sh 的ID。如果是这样,请不要运行它。

编辑 - 更多细节:

定时任务:

  • 从 git repo 获取,如果有任何变化,拉取并合并
  • 运行update.sh:

    #!/bin/sh
    ./commands.sh
    

我希望./commands.sh 只运行一次,因为下一小时,cron 将再次运行,从 git 中拉出,然后再次运行 update.sh。有没有办法让每个唯一的./commands.sh 只运行一次?

写完之后,也许最好的方法是只运行update.sh,如果 git repo 有变化?

这似乎有点矫枉过正且难以维护。有没有更简单的方法来编写只运行一次的 bash 脚本?

【问题讨论】:

  • 您的问题是commands.sh 的前一个实例在下次更新到期时可能仍在运行吗?或者还有谁在调用第二个实例?
  • 您是否尝试将cron 用作进程监督系统(负责保持守护程序运行,如果它退出则重新启动它,但从不启动多个实例)?因为最佳实践答案是“不要”——有真正为此目的而构建的流程监督服务,它们会做得更好。
  • ...同样,如果您的目标是每次将内容推送到 git... 时都运行一个命令...好吧,有 git 特定的方法可以做到这一点没有依赖 cron 的所有滞后和通用失败。
  • 对于小型嵌入式系统,runit 是您的最佳选择;它的作者做了一些努力来确保它可以与微小的libcs(比 glibc 小得多)一起工作。见jtimberman.housepub.org/blog/2012/12/29/…(第一个谷歌链接,但在浏览时,我喜欢它所说的)。
  • 你为什么不在完成后立即从系统中删除(删除)./commands.sh 文件。它再也不会运行了:)

标签: linux bash shell


【解决方案1】:

commands.sh 脚本的开头执行以下操作:

if [ -f stampfile ]; then
    exit
if
touch stampfile

这将检查一个名为stampfile 的文件(指定一个路径到任何方便的地方可以存储它)。如果它在那里,只需exit。如果不存在,请使用 touch 创建它。

然后让脚本做它的事情。

题外话: 这也可以用来避免同时运行两个脚本实例。然后脚本将在其运行结束时rm -f stampfile

在这种情况下,如果脚本被杀死,stampfile 将是“陈旧的”(存在标记但脚本不存在)。要检测过时的stampfile,请将脚本的 PID 放入其中而不是触摸它。

检查是否有另一个实例正在运行,并管理戳文件:

if [ -f stampfile ]; then
    if kill -0 $(<stampfile) 2>/dev/null; then
        exit
    else
        rm -f stampfile    # stale
    fi
fi
echo $$ >stampfile

# rest of script

rm -f stampfile

用户 @CharlesDuffy 指出,这种管理 stampfile 的方式可能容易出现 PID 重用问题,即陈旧的 stampfile 中的 PID 可能是与任何一个无关的正在运行的进程的这个。显然有a Linux utility called flock 可以让您以更强大的方式执行此操作。

【讨论】:

  • 基于 PID 的锁定检测非常不理想—— PID 决不能保证唯一。请考虑使用基于flock 的锁定,它也会在进程死亡时自动清除,包括通过重新启动或其他硬关闭情况。
  • @CharlesDuffy flock 在我的系统(OpenBSD 和 Mac OS X)上不可用,所以我无法测试它。对不起。我在 Mac 上确实有 shlock,但在 OpenBSD 上没有。任何便携式替代品?
  • 请参阅github.com/discoteq/flock 了解与util-linux 无关的实现。我还隐约记得有一个可用的lockfile 命令可以在过去使用flock()fcntl(LOCK_*)(可移植到Solaris、Linux,而且我不确定还有什么),但是不知道最近维护得有多积极。
  • @CharlesDuffy 我可能会,但由于这不是问题的主要问题,我不会更新答案。不过我会在里面做个记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-22
  • 1970-01-01
  • 2013-01-26
  • 2015-04-09
  • 2021-11-30
相关资源
最近更新 更多