防止长cron作业并行运行的通用解决方案?

我正在寻找一个简单而通用的解决方案,允许您在crontab中执行任何脚本或应用程序,并防止它运行两次。

解决方案应该独立于执行的命令。

我认为它应该看起来像lock && (command ; unlock) ,如果有另一个锁,lock将返回false。

第二部分就像是获取了lock,run命令并在执行命令后解锁,即使它返回错误。

看一下run-one 安装run-one 包。 从run-one命令的联机帮助页 Manpage图标

run-one是一个包装器脚本,它使用一组唯一的参数运行不超过一个命令的唯一实例。

当您希望一次只运行一个副本时,这对于cronjobs通常很有用。

就像timesudo ,你只需将它添加到命令中。 所以cronjob看起来像:

  */60 * * * * run-one rsync -azP $HOME example.com:/srv/backup 

有关更多信息和背景信息,请查看Dustin Kirkland 介绍它的博客文章 。

建立锁定的一种非常简单的方法:

 if mkdir /var/lock/mylock; then echo "Locking succeeded" >&2 else echo "Lock failed - exit" >&2 exit 1 fi 

想要运行的脚本需要创建锁。 如果锁存在,则另一个脚本正忙,因此第一个脚本无法运行。 如果该文件不存在,则没有脚本获取该锁。 所以当前脚本获取锁。 当脚本完成后,需要通过删除锁来重新释放锁。

有关bash锁的更多信息,请查看此页面

无需安装一些精美的包装:

 #!/bin/bash pgrep -xf "$*" > /dev/null || "$@" 

自己编写脚本比运行“apt-get install”更快,不是吗? 您可能希望将“-u $(id -u)”添加到pgrep以检查仅由当前用户运行的实例。

另请参阅Tim Kay的solo ,它通过绑定用户唯一的环回地址上的端口来执行锁定:

http://timkay.com/solo/

如果他的网站出现故障:

用法:

 solo -port=PORT COMMAND where PORT some arbitrary port number to be used for locking COMMAND shell command to run options -verbose be verbose -silent be silent 

像这样用它:

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

脚本:

 #!/usr/bin/perl -s # # solo v1.7 # Prevents multiple cron instances from running simultaneously. # # Copyright 2007-2016 Timothy Kay # http://timkay.com/solo/ # # It is free software; you can redistribute it and/or modify it under the terms of either: # # a) the GNU General Public License as published by the Free Software Foundation; # either version 1 (http://dev.perl.org/licenses/gpl1.html), or (at your option) # any later version (http://www.fsf.org/licenses/licenses.html#GNUGPL), or # # b) the "Artistic License" (http://dev.perl.org/licenses/artistic.html), or # # c) the MIT License (http://opensource.org/licenses/MIT) # use Socket; alarm $timeout if $timeout; $port =~ /^\d+$/ or $noport or die "Usage: $0 -port=PORT COMMAND\n"; if ($port) { # To work with OpenBSD: change to # $addr = pack(CnC, 127, 0, 1); # but make sure to use different ports across different users. # (Thanks to www.gotati.com .) $addr = pack(CnC, 127, $<, 1); print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n" if $verbose; $^F = 10; # unset close-on-exec socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "socket: $!"; bind(SOLO, sockaddr_in($port, $addr)) or $silent? exit: die "solo($port): $!\n"; } sleep $sleep if $sleep; exec @ARGV; 

你需要一把锁。 run-one完成了这项工作,但你也可能想从util-linux软件包中查看flock

它是内核开发人员提供的标准软件包,允许比run-one更多的自定义,并且仍然非常简单。

bash-hackers.org的一个简单的解决方案对我有用 ,就是使用mkdir 。 这是一种简单的方法,可以确保只运行一个程序实例。 使用返回的mkdir .lock创建一个目录

  • 如果创作成功,则为true
  • 如果锁定文件存在,则返回false ,表示当前正在运行一个实例。

所以这个简单的函数完成了所有文件锁定逻辑:

 if mkdir .lock; then echo "Locking succeeded" eval startYourProgram.sh ; else echo "Lock file exists. Program already running? Exit. " exit 1 fi echo "Program finished, Removing lock." rm -r .lock 

此解决方案适用于需要自行检查的bash脚本

 v=$(pgrep -xf "/bin/bash $0 $@") [ "${v/$BASHPID/}" != "" ] && exit 0