長いcronジョブが並行して実行されるのを防ぐ一般的なソリューションですか?


27

crontabで任意のスクリプトまたはアプリケーションを実行し、2回実行されるのを防ぐことができるシンプルで汎用的なソリューションを探しています。

解決策は、実行されたコマンドから独立している必要があります。

lock && (command ; unlock)別のロックがあった場合にロックがfalseを返すように見えるはずだと思います。

2番目の部分は、エラーを返したとしても、ロックを取得し、コマンドを実行した後にコマンドを実行し、ロックを解除するようなものです。

回答:


33

見てみましょう実行1のランワンをインストールパッケージを。コマンドマンページrun-oneマンページアイコンから:

run-oneは、一意の引数セットを持つコマンドの一意のインスタンスを1つだけ実行するラッパースクリプトです。

これは、一度に複数のコピーを実行したくない場合に、cronジョブでしばしば役立ちます。

同様timesudo、あなたはそれは、コマンドの前に追加します。したがって、cronjobは次のようになります。

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

詳細と背景については、Dustin Kirklandによるブログ記事をご覧ください。


5

ロックを設定する非常に簡単な方法:

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

実行したいスクリプトはロックを作成する必要があります。ロックが存在する場合、別のスクリプトがビジーであるため、最初のスクリプトは実行できません。ファイルが存在しない場合、スクリプトはロックを取得していません。そのため、現在のスクリプトがロックを取得します。スクリプトが終了したら、ロックを解除してロックを解除する必要があります。

bashロックの詳細については、このページを確認しください


1
終了時にロックを解除するEXITトラップも必要です。echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
ガイラ

1
理想的には、サブタスクとして必要なコマンドを実行するプロセスからアドバイザリフロックを使用する必要があります。そうすれば、彼らがすべて死ぬと、群れは自動的に解放されますが、ロックファイルの存在を使用しても群れは解放されません。ネットワークポートを使用すると、同じように働くだろう-それはだけれども方法の問題である小さな名前空間、。
アレックスノースキーズ

3

豪華なパッケージをインストールする必要はありません:

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

「apt-get install」を実行するよりも、自分でそのスクリプトを書く方が速いのではないでしょうか?「-u $(id -u)」をpgrepに追加して、現在のユーザーのみによって実行されているインスタンスをチェックすることができます。


2
これは、単一のインスタンスを保証するものではありません。||どちらかがスクリプトを開始する前に、2つのスクリプトを同時にオペレーターの反対側に渡すことができます。
セダットカパノグル

@SedatKapanoglu確かに、このスクリプトは競合条件に耐えることはできませんが、元々の質問は長時間実行されるcronジョブ(最大で1分間に1回実行される)に関するものでした。システムがプロセスの作成に1分以上必要な場合、他の問題がいくつかあります。ただし、他の何らかの理由で必要な場合は、flock(1)を使用して、上記のスクリプトを競合状態から保護できます。
マイケルコーハン16

これを使用しましたが、それ自体を確認する必要があるbashスクリプトに使用しました。コードは次のとおりです。v = $(pgrep -xf "/ bin / bash $ 0 $ @")["$ {v / $ BASHPID /}"!= ""] && exit 2
ahofmann

3

soloユーザーに固有のループバックアドレスにポートをバインドしてロックを実行するTim Kay'sも参照してください。

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;

Mac OSXのsolo(3801): Can't assign requested address場合0、methodの3番目のパラメーターにa を強制しない限り、これはエラーで失敗しますpack。BSDにとって良いことは、Macにとっても良いことです。
エリックレシンスキー

1

ロックが必要です。run-one仕事をしますがflockutil-linuxパッケージから見てもよいでしょう。

カーネル開発者が提供する標準パッケージであり、より多くのカスタマイズが可能でrun-oneあり、非常に簡単です。


0

私のために働いたbash-hackers.orgからの簡単な解決策はmkdirを使用することでした。これは、プログラムの1つのインスタンスのみが実行されていることを確認する簡単な方法です。mkdir .lockでディレクトリを作成します

  • 作成が成功した場合はtrue
  • ロックファイルが存在する場合はfalse。現在1つのインスタンスが実行されていることを示します。

そのため、この単純な関数はすべてのファイルロックロジックを実行しました。

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

0

このソリューションは、自身をチェックする必要があるbashスクリプト用です

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.