问题描述
我正在寻找一个简单而通用的解决方案,它允许您在 crontab 中执行任何脚本或应用程序并防止它运行两次。
解决方案应该独立于执行的命令。
我认为它应该看起来像 lock && (command ; unlock)
,如果有另一个锁,锁将返回 false。
第二部分就像如果它获取了锁,运行命令并在命令执行后解锁,即使它返回错误。
最佳方法
看看 run-one
包。从 run-one
命令的联机帮助页:
run-one is a wrapper script that runs no more than one unique instance of some command with a unique set of arguments.
This is often useful with cronjobs, when you want no more than one copy running at a time.
像 time
或 sudo
一样,您只需将其添加到命令中即可。所以一个 cronjob 可能看起来像:
*/60 * * * * run-one rsync -azP $HOME example.com:/srv/backup
有关更多信息和背景,请查看 Dustin Kirkland 的 the blog post introducing it。
次佳方法
设置锁的一种非常简单的方法:
if mkdir /var/lock/mylock; then
echo "Locking succeeded" >&2
else
echo "Lock failed - exit" >&2
exit 1
fi
想要运行的脚本需要创建锁。如果锁存在,则另一个脚本正忙,因此第一个脚本无法运行。如果文件不存在,则没有脚本获得锁定。所以当前脚本获取锁。当脚本完成后,需要通过移除锁来释放锁。
有关 bash 锁的更多信息,请查看 this 页面
第三种方法
无需安装一些花哨的包:
#!/bin/bash
pgrep -xf "$*" > /dev/null || "$@"
自己编写脚本比运行“apt-get install”更快,不是吗?您可能希望将“-u $(id -u)”添加到 pgrep 以仅检查当前用户运行的实例。
第四种方法
另请参阅 Tim Kay 的 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;