当前位置: 首页>>技术教程>>正文


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

,

问题描述

我正在寻找一个简单而通用的解决方案,它允许您在 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.

timesudo 一样,您只需将其添加到命令中即可。所以一个 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 ,它通过在用户唯一的回送地址上绑定端口来执行锁定:

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;

参考资料

本文由Ubuntu问答整理, 博文地址: https://ubuntuqa.com/article/12113.html,未经允许,请勿转载。