पायथन में मुझे क्रोन जैसे शेड्यूलर कैसे मिलते हैं? [बन्द है]


348

मैं पायथन में एक पुस्तकालय की तलाश कर रहा हूं जो कि कार्यक्षमता प्रदान करेगा atऔर cronपसंद करेगा ।

मुझे बॉक्स पर स्थापित टूल पर भरोसा करने के बजाय, एक शुद्ध पायथन समाधान चाहिए; इस तरह मैं बिना क्रोन वाली मशीनों पर चलता हूं।

उन अपरिचित लोगों के लिए cron: आप एक अभिव्यक्ति के आधार पर कार्यों को निर्धारित कर सकते हैं जैसे:

 0 2 * * 7 /usr/bin/run-backup # run the backups at 0200 on Every Sunday
 0 9-17/2 * * 1-5 /usr/bin/purge-temps # run the purge temps command, every 2 hours between 9am and 5pm on Mondays to Fridays.

क्रोन समय अभिव्यक्ति सिंटैक्स कम महत्वपूर्ण है, लेकिन मैं इस तरह के लचीलेपन के साथ कुछ करना चाहता हूं।

अगर ऐसा कुछ नहीं है जो मेरे लिए आउट-द-बॉक्स करता है, तो बिल्डिंग ब्लॉक्स के लिए कुछ इस तरह का सुझाव देने के लिए आभार प्राप्त होगा।

संपादित करें मुझे प्रक्रियाओं को लॉन्च करने में कोई दिलचस्पी नहीं है, सिर्फ पायथन कार्यों में "जॉब्स" को भी लिखा गया है। आवश्यकता से मुझे लगता है कि यह एक अलग धागा होगा, लेकिन एक अलग प्रक्रिया में नहीं।

यह अंत करने के लिए, मैं क्रोन समय की अभिव्यक्ति की अभिव्यक्ति की तलाश कर रहा हूं, लेकिन पायथन में।

क्रॉन है साल के लिए चारों ओर हो गया है, लेकिन मैं संभव के रूप में पोर्टेबल के रूप में होने की कोशिश कर रहा हूँ। मैं इसकी उपस्थिति पर भरोसा नहीं कर सकता।


1
मैं यह भी जानना चाहूंगा कि यह कैसे करना है। प्लेटफ़ॉर्म विशिष्ट घटकों पर निर्भर रहने की तुलना में क्रॉस प्लेटफ़ॉर्म समाधान होना अधिक उपयोगी होगा।
शॉन

7
यह ऑफ-टॉपिक नहीं है, यह एक बहुत ही महत्वपूर्ण और उपयोगी सवाल है
कॉनर

जवाबों:


571

यदि आप कुछ हल्के चेकआउट शेड्यूल की तलाश कर रहे हैं :

import schedule
import time

def job():
    print("I'm working...")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)

while 1:
    schedule.run_pending()
    time.sleep(1)

प्रकटीकरण : मैं उस पुस्तकालय का लेखक हूं।


7
आपको उल्लेख करना चाहिए कि आप इसके अनुरक्षक हैं schedule। इसने मेरे लिए अच्छा काम किया। यह और भी अच्छा होगा यदि इसमें सिंटैक्स और समर्थित डेकोरेटर जैसे क्रोन होते हैं (क्रायथॉन देखें लेकिन इस लाइब्रेरी का उपयोग न करें यह काम नहीं करता है; शेड्यूलिंग अच्छा नहीं लिखा गया है)।
टिम लुडविंस्की

23
क्या नौकरी के लिए एक पैरामीटर पास करने का कोई तरीका है? मैं इस तरह से कुछ करना चाहता हूं: शिड्यूल.इवरी ()। घंटे.दो (नौकरी (मायपरम))
ज़ेन स्कंकवर्क्स

5
अनुसूची। हर ()। घंटे। (नौकरी) यह हर घड़ी घंटे चलाता है? जैसे 01:00, 02:00, 03:00, आदि? भले ही शुरुआत का समय पूरा एक घंटा न हो?
स्वेटेक

1
मान लीजिए कि यह कोड अनुसूचक थिंकपैड में है। क्या यह कोड अपने आप चलेगा?
किशन

25
@ darrel-holt और @ zen-skunkworx: do()फंक्शन फॉरवर्ड अतिरिक्त तर्कों को आप इसे जॉब फंक्शन में पास करते हैं : शिड्यूल। ट्रेडथेड्स .io/en/stable/api.html# schedule.Job.do उदाहरण के लिए, आप यह कर सकते हैं : schedule.every().hour.do(job, param1, param2)लंबोदर का उपयोग करने की आवश्यकता नहीं है। आशा है कि यह मदद करता है :)
dbader

65

आप अपने crontab को निर्दिष्ट करने के लिए सिंटैक्स पास करने वाले सामान्य पायथन तर्क का उपयोग कर सकते हैं। उदाहरण के लिए, मान लें कि हम नीचे एक घटना वर्ग को परिभाषित करते हैं:

from datetime import datetime, timedelta
import time

# Some utility classes / functions first
class AllMatch(set):
    """Universal set - match everything"""
    def __contains__(self, item): return True

allMatch = AllMatch()

def conv_to_set(obj):  # Allow single integer to be provided
    if isinstance(obj, (int,long)):
        return set([obj])  # Single item
    if not isinstance(obj, set):
        obj = set(obj)
    return obj

# The actual Event class
class Event(object):
    def __init__(self, action, min=allMatch, hour=allMatch, 
                       day=allMatch, month=allMatch, dow=allMatch, 
                       args=(), kwargs={}):
        self.mins = conv_to_set(min)
        self.hours= conv_to_set(hour)
        self.days = conv_to_set(day)
        self.months = conv_to_set(month)
        self.dow = conv_to_set(dow)
        self.action = action
        self.args = args
        self.kwargs = kwargs

    def matchtime(self, t):
        """Return True if this event should trigger at the specified datetime"""
        return ((t.minute     in self.mins) and
                (t.hour       in self.hours) and
                (t.day        in self.days) and
                (t.month      in self.months) and
                (t.weekday()  in self.dow))

    def check(self, t):
        if self.matchtime(t):
            self.action(*self.args, **self.kwargs)

(नोट: पूरी तरह से परीक्षण नहीं)

फिर आपके क्रोनटैब को सामान्य अजगर सिंटैक्स में निर्दिष्ट किया जा सकता है:

c = CronTab(
  Event(perform_backup, 0, 2, dow=6 ),
  Event(purge_temps, 0, range(9,18,2), dow=range(0,5))
)

इस तरह आपको पायथन के तर्क यांत्रिकी की पूरी शक्ति मिलती है (स्थिति और कीवर्ड को मिलाकर, और सप्ताह और महीनों के नामों के लिए प्रतीकात्मक नामों का उपयोग किया जा सकता है)

क्रोनटैब क्लास को प्रत्येक घटना पर मिनट वेतन वृद्धि, और कॉलिंग चेक () में सोते हुए परिभाषित किया जाएगा। (हालांकि दिन के उजाले की बचत के समय के साथ कुछ सूक्ष्मताएं हैं / हालांकि समय से सावधान रहना होगा)। यहाँ एक त्वरित कार्यान्वयन है:

class CronTab(object):
    def __init__(self, *events):
        self.events = events

    def run(self):
        t=datetime(*datetime.now().timetuple()[:5])
        while 1:
            for e in self.events:
                e.check(t)

            t += timedelta(minutes=1)
            while datetime.now() < t:
                time.sleep((t - datetime.now()).seconds)

ध्यान देने योग्य कुछ बातें: पायथन के सप्ताह / महीने शून्य अनुक्रमित (क्रोन के विपरीत) हैं, और यह सीमा अंतिम तत्व को बाहर करती है, इसलिए "1-5" जैसे वाक्यविन्यास सीमा (0,5) हो जाती है - अर्थात [0,1,2, 3,4]। यदि आप क्रोन सिंटैक्स पसंद करते हैं, तो इसे पार्स करना बहुत मुश्किल नहीं है।


आप अनुभवहीन के लिए कुछ आयात विवरण जोड़ना चाह सकते हैं। मैं एक ही फाइल में सभी वर्गों को डेटाइम इंपोर्ट से * इंपोर्ट स्लीप से और सोते समय में बदल दिया। अच्छा, सरल सुरुचिपूर्ण समाधान। धन्यवाद।
मावन

1
बस सोच रहा था, क्रोनोस पर यह क्यों पसंद किया जा रहा है? क्या वह छोटी गाड़ी है (चूंकि क्रोनोस शेड्यूल का उपयोग करता है)? या यह सिर्फ पुराना है?
cregox

धन्यवाद ब्रायन, मैं उत्पादन में आपके समाधान का उपयोग करता हूं और यह काफी अच्छी तरह से काम कर रहा है। हालाँकि, जैसा कि अन्य ने बताया है, आपके रन कोड में एक सूक्ष्म बग है। इसके अलावा, मैंने इसे ज़रूरतों के लिए अत्यधिक जटिल पाया।
रफ.मैयर

1
यह अच्छा है, लेकिन फिर भी हर घंटे, मिनट आदि के निष्पादन के लिए स्लैश नोटेशन का समर्थन नहीं करता है ...
क्रिस कोस्टन

1
अपने स्वयं के वर्गों को लिखने के लिए उत्कृष्ट विचार, उदाहरण के लिए, जब मेरे पास एक सर्वर पर sudo की पहुंच नहीं है और इस तरह से नहीं हो सकता है pip install anything:)
Cometsong


27

एक चीज जो मैंने अपनी खोजों में देखी है, वह है अजगर का schedमॉड्यूल जो उस तरह का हो सकता है जिसे आप खोज रहे हैं।


11
शेड्यूल में अब एंटैब्स () है जो पूर्ण शेड्यूलिंग करता है।
जेरथ

5
मुझे उम्मीद है कि यह पसंदीदा उत्तर होगा क्योंकि शेड्यूल अभी python2 और 3 stdlib का हिस्सा है और यह पूर्ण समय-निर्धारण करता है।
माइकल

20

"... Crontab फ़ाइलों को पढ़ने और लिखने के लिए Crontab मॉड्यूल और स्वचालित रूप से सीधे API का उपयोग करके सिस्टम cron तक पहुँचना।"

http://pypi.python.org/pypi/python-crontab

और APScheduler, एक अजगर पैकेज भी। पहले से ही लिखित और डीबग किया गया।

http://packages.python.org/APScheduler/cronschedule.html


1
ओपी ने विशेष रूप से कुछ के लिए कहा जो कि क्रोन के बिना सिस्टम पर काम करेगा
हमी

11

अधिक या कम समान रूप से ऊपर लेकिन समवर्ती का उपयोग करते हुए gevent :)

"""Gevent based crontab implementation"""

from datetime import datetime, timedelta
import gevent

# Some utility classes / functions first
def conv_to_set(obj):
    """Converts to set allowing single integer to be provided"""

    if isinstance(obj, (int, long)):
        return set([obj])  # Single item
    if not isinstance(obj, set):
        obj = set(obj)
    return obj

class AllMatch(set):
    """Universal set - match everything"""
    def __contains__(self, item): 
        return True

allMatch = AllMatch()

class Event(object):
    """The Actual Event Class"""

    def __init__(self, action, minute=allMatch, hour=allMatch, 
                       day=allMatch, month=allMatch, daysofweek=allMatch, 
                       args=(), kwargs={}):
        self.mins = conv_to_set(minute)
        self.hours = conv_to_set(hour)
        self.days = conv_to_set(day)
        self.months = conv_to_set(month)
        self.daysofweek = conv_to_set(daysofweek)
        self.action = action
        self.args = args
        self.kwargs = kwargs

    def matchtime(self, t1):
        """Return True if this event should trigger at the specified datetime"""
        return ((t1.minute     in self.mins) and
                (t1.hour       in self.hours) and
                (t1.day        in self.days) and
                (t1.month      in self.months) and
                (t1.weekday()  in self.daysofweek))

    def check(self, t):
        """Check and run action if needed"""

        if self.matchtime(t):
            self.action(*self.args, **self.kwargs)

class CronTab(object):
    """The crontab implementation"""

    def __init__(self, *events):
        self.events = events

    def _check(self):
        """Check all events in separate greenlets"""

        t1 = datetime(*datetime.now().timetuple()[:5])
        for event in self.events:
            gevent.spawn(event.check, t1)

        t1 += timedelta(minutes=1)
        s1 = (t1 - datetime.now()).seconds + 1
        print "Checking again in %s seconds" % s1
        job = gevent.spawn_later(s1, self._check)

    def run(self):
        """Run the cron forever"""

        self._check()
        while True:
            gevent.sleep(60)

import os 
def test_task():
    """Just an example that sends a bell and asd to all terminals"""

    os.system('echo asd | wall')  

cron = CronTab(
  Event(test_task, 22, 1 ),
  Event(test_task, 0, range(9,18,2), daysofweek=range(0,5)),
)
cron.run()

बस एक नोट है कि datetime.timetuple () वर्ष, महीने, दिन ... आदि के साथ शुरू होगा ...
ट्रे स्टाउट

9

सूचीबद्ध समाधानों में से कोई भी जटिल क्रोन शेड्यूल स्ट्रिंग को पार्स करने का प्रयास नहीं करता है। तो, यहाँ मेरा संस्करण है, क्रोनिटर का उपयोग करके । बेसिक जिस्ट:

schedule = "*/5 * * * *" # Run every five minutes

nextRunTime = getNextCronRunTime(schedule)
while True:
     roundedDownTime = roundDownTime()
     if (roundedDownTime == nextRunTime):
         ####################################
         ### Do your periodic thing here. ###
         ####################################
         nextRunTime = getNextCronRunTime(schedule)
     elif (roundedDownTime > nextRunTime):
         # We missed an execution. Error. Re initialize.
         nextRunTime = getNextCronRunTime(schedule)
     sleepTillTopOfNextMinute()

सहायक दिनचर्या:

from croniter import croniter
from datetime import datetime, timedelta

# Round time down to the top of the previous minute
def roundDownTime(dt=None, dateDelta=timedelta(minutes=1)):
    roundTo = dateDelta.total_seconds()
    if dt == None : dt = datetime.now()
    seconds = (dt - dt.min).seconds
    rounding = (seconds+roundTo/2) // roundTo * roundTo
    return dt + timedelta(0,rounding-seconds,-dt.microsecond)

# Get next run time from now, based on schedule specified by cron string
def getNextCronRunTime(schedule):
    return croniter(schedule, datetime.now()).get_next(datetime)

# Sleep till the top of the next minute
def sleepTillTopOfNextMinute():
    t = datetime.utcnow()
    sleeptime = 60 - (t.second + t.microsecond/1000000.0)
    time.sleep(sleeptime)

कैसे कोई "एक निष्पादन याद किया" में जा सकता है elif? एटीएम मैं एक शेड्यूल का उपयोग कर रहा हूं, जैसे "समय-समय पर अपनी बात करें" में 1 मिनट से अधिक "* * * * *"जोड़ना , लेकिन मैं हमेशा उस विवरण में सामान देखता हूं। जब मुझे 1 मिनट से अधिक समय हो जाता है, तो मैं बस उस लूप को गायब करने वाले लूप निष्पादन को देखता हूं। time.sleepif
टीपीपीजेड

@TPPZ इस प्रक्रिया को निलंबित किया जा सकता था, घड़ी को मैन्युअल रूप से या ntp आदि से बदला जा सकता था। Croniter का उपयोग Airflow में किया जाता है और यह Crontab मॉड्यूल और अन्य की तुलना में अधिक भरा हुआ लगता है।
dlamblin

7

मैंने स्क्रिप्ट को संशोधित किया है।

  1. प्रयोग करने में आसान:

    cron = Cron()
    cron.add('* * * * *'   , minute_task) # every minute
    cron.add('33 * * * *'  , day_task)    # every hour
    cron.add('34 18 * * *' , day_task)    # every day
    cron.run()
  2. एक मिनट के पहले सेकंड में कार्य शुरू करने का प्रयास करें।

जीथब पर कोड


6

ब्रायन द्वारा सुझाए गए क्रोनटैब क्लास रन विधि के लिए मेरे पास एक मामूली सुधार है ।

प्रत्येक मिनट के अंत में एक सेकंड के लिए एक सेकंड, हार्ड लूप के कारण समय समाप्त हो गया।

class CronTab(object):
    def __init__(self, *events):
        self.events = events

    def run(self):
        t=datetime(*datetime.now().timetuple()[:5])
        while 1:
            for e in self.events:
                e.check(t)

            t += timedelta(minutes=1)
            n = datetime.now()
            while n < t:
                s = (t - n).seconds + 1
                time.sleep(s)
                n = datetime.now()

4

ऐसा करने के लिए "शुद्ध अजगर" तरीका नहीं है क्योंकि आपके समाधान को चलाने के लिए कुछ अन्य प्रक्रिया को अजगर लॉन्च करना होगा। प्रत्येक प्लेटफ़ॉर्म में प्रक्रियाओं को लॉन्च करने और उनकी प्रगति की निगरानी करने के लिए एक या बीस अलग-अलग तरीके होंगे। यूनिक्स प्लेटफार्मों पर, क्रोन पुराना मानक है। Mac OS X पर भी लॉन्च किया गया है, जो क्रोन-जैसे लॉन्चिंग को वॉचडॉग कार्यक्षमता के साथ जोड़ता है जो कि यदि आप चाहते हैं तो आपकी प्रक्रिया को जीवित रख सकते हैं। एक बार अजगर चल रहा है, तो आप शेड्यूल मॉड्यूल का उपयोग कार्यों को शेड्यूल करने के लिए कर सकते हैं ।


4

मुझे पता है कि बहुत सारे उत्तर हैं, लेकिन एक और समाधान सज्जाकारों के साथ जा सकता है । यह एक विशिष्ट समय में हर रोज एक कार्य को दोहराने के लिए एक उदाहरण है। इस तरीके का उपयोग करने के बारे में शांत सोच यह है कि आपको केवल उस फ़ंक्शन में सिंटैक्टिक शुगर को जोड़ना होगा जिसे आप शेड्यूल करना चाहते हैं:

@repeatEveryDay(hour=6, minutes=30)
def sayHello(name):
    print(f"Hello {name}")

sayHello("Bob") # Now this function will be invoked every day at 6.30 a.m

और डेकोरेटर की तरह दिखेगा:

def repeatEveryDay(hour, minutes=0, seconds=0):
    """
    Decorator that will run the decorated function everyday at that hour, minutes and seconds.
    :param hour: 0-24
    :param minutes: 0-60 (Optional)
    :param seconds: 0-60 (Optional)
    """
    def decoratorRepeat(func):

        @functools.wraps(func)
        def wrapperRepeat(*args, **kwargs):

            def getLocalTime():
                return datetime.datetime.fromtimestamp(time.mktime(time.localtime()))

            # Get the datetime of the first function call
            td = datetime.timedelta(seconds=15)
            if wrapperRepeat.nextSent == None:
                now = getLocalTime()
                wrapperRepeat.nextSent = datetime.datetime(now.year, now.month, now.day, hour, minutes, seconds)
                if wrapperRepeat.nextSent < now:
                    wrapperRepeat.nextSent += td

            # Waiting till next day
            while getLocalTime() < wrapperRepeat.nextSent:
                time.sleep(1)

            # Call the function
            func(*args, **kwargs)

            # Get the datetime of the next function call
            wrapperRepeat.nextSent += td
            wrapperRepeat(*args, **kwargs)

        wrapperRepeat.nextSent = None
        return wrapperRepeat

    return decoratorRepeat

1

ब्रायन का समाधान काफी अच्छा काम कर रहा है। हालांकि, जैसा कि अन्य ने बताया है, रन कोड में एक सूक्ष्म बग है। इसके अलावा, मैंने इसे ज़रूरतों के लिए अत्यधिक जटिल पाया।

यदि किसी को इसकी आवश्यकता है तो रन कोड के लिए मेरा सरल और कार्यात्मक विकल्प है:

def run(self):
    while 1:
        t = datetime.now()
        for e in self.events:
            e.check(t)

        time.sleep(60 - t.second - t.microsecond / 1000000.0)

1

एक और तुच्छ समाधान होगा:

from aqcron import At
from time import sleep
from datetime import datetime

# Event scheduling
event_1 = At( second=5 )
event_2 = At( second=[0,20,40] )

while True:
    now = datetime.now()

    # Event check
    if now in event_1: print "event_1"
    if now in event_2: print "event_2"

    sleep(1)

और वर्ग aqcron.At है:

# aqcron.py

class At(object):
    def __init__(self, year=None,    month=None,
                 day=None,     weekday=None,
                 hour=None,    minute=None,
                 second=None):
        loc = locals()
        loc.pop("self")
        self.at = dict((k, v) for k, v in loc.iteritems() if v != None)

    def __contains__(self, now):
        for k in self.at.keys():
            try:
                if not getattr(now, k) in self.at[k]: return False
            except TypeError:
                if self.at[k] != getattr(now, k): return False
        return True

1
कई प्रश्नों के बायलरप्लेट / वर्बेटिम उत्तर कॉपी और पेस्ट करते समय सावधान रहें, ये समुदाय द्वारा "स्पैमी" के रूप में चिह्नित किए जाते हैं। यदि आप ऐसा कर रहे हैं, तो इसका आमतौर पर मतलब है कि प्रश्न डुप्लिकेट हैं इसलिए उन्हें इस तरह से चिह्नित करें: stackoverflow.com/a/12360556/419
Kev

1

यदि आप एक डिस्ट्रिब्यूटेड शेड्यूलर की तलाश में हैं, तो आप https://github.com/sherinkurian/mani की जांच कर सकते हैं - इसके लिए रेडिस की जरूरत नहीं है, लेकिन जो आप देख रहे हैं वह ऐसा नहीं हो सकता है। (ध्यान दें कि मैं लेखक हूं) यह एक से अधिक नोड पर घड़ी चलाने से दोष-सहिष्णुता सुनिश्चित करने के लिए बनाया गया था।


0

मुझे नहीं पता कि ऐसा कुछ पहले से मौजूद है। समय, डेटाइम और / या कैलेंडर मॉड्यूल के साथ अपना खुद का लिखना आसान होगा, http://docs.python.org/l.net/n.html देखें

एक अजगर समाधान के लिए एकमात्र चिंता यह है कि आपकी नौकरी को हमेशा चलने की जरूरत है और संभवतः रिबूट के बाद स्वचालित रूप से "पुनर्जीवित" होना चाहिए, जिसके लिए आपको सिस्टम पर निर्भर समाधानों पर भरोसा करने की आवश्यकता है।


3
अपना खुद का रोल करें एक विकल्प है - हालांकि सबसे अच्छा कोड वह कोड है जिसे आपको लिखना नहीं है। पुनरुत्थान, मुझे लगता है कि कुछ ऐसा है जिस पर मुझे विचार करने की आवश्यकता हो सकती है।
jamesh

0

आप PiCloud के [1] क्रोन [2] की जांच कर सकते हैं, लेकिन ध्यान दें कि आपकी नौकरियां आपके ही मशीन पर नहीं चल रही होंगी। यह एक सेवा भी है जिसके लिए आपको भुगतान करने की आवश्यकता होगी यदि आप महीने में 20 घंटे से अधिक गणना समय का उपयोग करते हैं।

[१] http://www.picloud.com

[२] http://docs.picloud.com/cron.html


0

सर्वर पर Crontab की विधि।

पायथन फ़ाइल का नाम hello.py

Step1: एक sh फाइल बनाएं जो नाम s.sh दे

python3 /home/ubuntu/Shaurya/Folder/hello.py> /home/ubuntu/Shaurya/Folder/log.txt 2> & 1

Step2: ओपन Crontab संपादक

Crontab -e

चरण 3: अनुसूची समय जोड़ें

Crontab स्वरूपण का उपयोग करें

2 * * *

यह क्रोन "2 मिनट पर" चलेगा


0

मुझे पसंद है कि कैसे pycron पैकेज इस समस्या को हल करता है।

import pycron
import time

while True:
    if pycron.is_now('0 2 * * 0'):   # True Every Sunday at 02:00
        print('running backup')
    time.sleep(60)

1
यह एक अच्छा विचार नहीं है, क्योंकि आपका कोड "प्रिंट ('बैकअप चल रहा है)" पूरे मिनट 5 एस अंतराल के साथ लॉन्च होगा। तो इस मामले में देरी 60 सेकंड होनी चाहिए।
n158

आप सही हे! यह बात बताने के लिए धन्यवाद।
डफौ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.