एक लंबी क्रोन नौकरी को समानांतर में चलने से रोकने के लिए सामान्य समाधान?


27

मुझे एक सरल और सामान्य समाधान की तलाश है जो आपको किसी भी स्क्रिप्ट या एप्लिकेशन को क्रॉस्टैब में निष्पादित करने और दो बार चलने से रोकने की अनुमति देगा।

समाधान निष्पादित कमांड पर स्वतंत्र होना चाहिए।

मुझे लगता है कि यह देखना चाहिए कि lock && (command ; unlock)जहां एक और ताला था , वहां ताला गलत होगा।

दूसरा भाग ऐसा होगा यदि यह लॉक, रन कमांड और अनलॉक होने के बाद कमांड को निष्पादित करता है, भले ही वह त्रुटि देता हो।

जवाबों:


33

रन-वनरन-वन स्थापित करें पैकेज पर एक नजर । कमांड के लिए मैनपेजrun-oneमैनपेज आइकन से :

रन-वन एक रैपर स्क्रिप्ट है जो कि कुछ कमांड के एक से अधिक अद्वितीय उदाहरणों के एक अद्वितीय सेट के साथ चलती है।

यह अक्सर क्रोनोजर के साथ उपयोगी होता है, जब आप एक बार में एक से अधिक कॉपी नहीं चलाना चाहते हैं।

जैसे timeया sudo, आप इसे केवल कमांड के लिए प्रस्तुत करते हैं। तो एक क्रोनजोब जैसा दिख सकता है:

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

अधिक जानकारी और पृष्ठभूमि के लिए, डस्टिन किर्कलैंड द्वारा प्रस्तुत ब्लॉग पोस्ट देखें


5

एक ताला बसाने का एक बहुत ही सरल तरीका:

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

एक स्क्रिप्ट जो जरूरतों को चलाने के लिए चाहता है ताला बना सकता है। यदि लॉक मौजूद है, तो दूसरी स्क्रिप्ट व्यस्त है, इसलिए पहली स्क्रिप्ट नहीं चल सकती है। यदि फ़ाइल मौजूद नहीं है, तो किसी भी स्क्रिप्ट ने लॉक का अधिग्रहण नहीं किया है। इसलिए वर्तमान स्क्रिप्ट लॉक को प्राप्त करती है। जब स्क्रिप्ट समाप्त हो गई है तो लॉक को हटाकर लॉक को फिर से खोलना होगा।

बैश ताले के बारे में अधिक जानकारी के लिए, इस पृष्ठ को देखें


1
आप एक EXIT जाल भी चाहते हैं जो बाहर निकलने पर ताला हटा दे। echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
जिरह

1
आदर्श रूप से आप एक प्रक्रिया से सलाहकार झुंड का उपयोग करना चाहेंगे जो एक उपमा के रूप में इच्छित कमांड चलाता है। इस तरह अगर वे सभी मर जाते हैं तो झुंड स्वचालित रूप से रिलीज़ हो जाता है, जो लॉक फ़ाइल की उपस्थिति का उपयोग नहीं करता है। एक नेटवर्क पोर्ट का उपयोग कर एक समान तरीके से काम करेगा - हालांकि कि एक है जिस तरह से छोटे नाम स्थान है, जो एक समस्या है।
एलेक्स नॉर्थ-कीज

3

कुछ फैंसी पैकेज स्थापित करने की आवश्यकता नहीं है:

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

यह उस स्क्रिप्ट को लिखने के लिए तेजी से है जो "एप्ट-गेट इंस्टॉल" को चलाने के बजाय है, है न? आप केवल वर्तमान उपयोगकर्ता द्वारा चलाए जाने वाले उदाहरणों के लिए pgrep में "-u $ (id -u)" जोड़ना चाह सकते हैं।


2
यह एक भी उदाहरण की गारंटी नहीं देता है। दो स्क्रिप्ट ||एक ही समय में ऑपरेटर के दूसरी तरफ से गुजर सकते हैं , इससे पहले या तो स्क्रिप्ट शुरू करने का मौका है।
सेदत कपपनोग्लू

@SedatKapanoglu दी, वह स्क्रिप्ट रेसकंडिशन-प्रूफ नहीं है, लेकिन मूल प्रश्न लंबे समय तक चलने वाली क्रॉन-जॉब्स (जो एक मिनट में सबसे अधिक बार चलाया जाता है) के बारे में था। यदि आपके सिस्टम को प्रक्रिया के निर्माण के लिए एक मिनट से अधिक की आवश्यकता है, तो आपके पास कुछ अन्य मुद्दे हैं। हालांकि, अगर कुछ अन्य कारणों से आपको दौड़ की स्थिति के खिलाफ स्क्रिप्ट से बचाने के लिए झुंड (1) का उपयोग किया जा सकता है।
माइकल कॉहान

मैंने इसका इस्तेमाल किया लेकिन एक बैश स्क्रिप्ट के लिए जिसे खुद की जांच करनी चाहिए। कोड यह है: v = $ (pgrep -xf "/ bin / bash $ 0 $ @") ["$ {v / $ BASHPID /}"! = ""] && से बाहर निकलें 2
ahofmann

3

टिम कय को भी देखें 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;

मैक OSX के लिए, यह त्रुटि के साथ विफल होगा solo(3801): Can't assign requested addressजब तक कि आप 0विधि के 3 पैरामीटर के लिए मजबूर न करें pack। जो बीएसडी के लिए अच्छा है वह मैक के लिए भी अच्छा है।
एरिक लेसचिन्स्की

1

आपको ताला चाहिए। run-oneकाम करता है, लेकिन आप पैकेज flockसे भी देखना चाह सकते हैं util-linux

यह कर्नेल डेवलपर्स द्वारा प्रदान किया गया एक मानक पैकेज है, की तुलना में अधिक अनुकूलन की अनुमति देता है run-oneऔर अभी भी बहुत सरल है।


0

Bash-hackers.org का एक सरल समाधान जो मेरे लिए काम करता था, mkdir का उपयोग कर रहा था । यह एक आसान तरीका है कि यह कैसे सुनिश्चित करें कि आपके प्रोग्राम का केवल एक उदाहरण चल रहा है। Mkdir .lock के साथ एक निर्देशिका बनाएँ जो लौटती है

  • सच है अगर निर्माण सफल था और
  • गलत है अगर लॉक फ़ाइल मौजूद है, यह दर्शाता है कि वर्तमान में एक उदाहरण चल रहा है।

तो इस साधारण फ़ंक्शन ने सभी फ़ाइल लॉकिंग लॉजिक किया:

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

यह समाधान एक बैश स्क्रिप्ट के लिए है जिसे स्वयं को जांचने की आवश्यकता है

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.