파이썬에서 스케줄러와 같은 Cron을 얻는 방법은 무엇입니까? [닫은]


348

기능 을 제공 at하고 cron좋아할 파이썬 라이브러리를 찾고 있습니다.

상자에 설치된 도구에 의존하기보다는 순수한 파이썬 솔루션을 원합니다. 이 방법으로 나는 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.

cron 시간 표현 구문은 덜 중요하지만 이러한 종류의 유연성을 가진 것을 원합니다.

기본적으로 나를 위해 이것을 수행하는 것이 없다면 빌딩 블록이 이와 같은 것을 만들기위한 제안은 감사하게받을 것입니다.

편집 프로세스 시작에 관심이없고 파이썬으로 작성된 "작업"-파이썬 함수에 관심이 있습니다. 필연적으로 나는 이것이 다른 스레드 일 것이라고 생각하지만 다른 프로세스에는 없습니다.

이를 위해 cron 시간 표현의 표현력을 찾고 있지만 Python에서는 찾고 있습니다.

Cron 수년 동안 사용되어 왔지만 가능한 한 휴대하기 위해 노력하고 있습니다. 나는 그 존재에 의존 할 수 없습니다.


1
또한이 작업을 수행하는 방법을 알고 싶습니다. 플랫폼 별 구성 요소에 의존하는 것보다 크로스 플랫폼 솔루션을 사용하는 것이 더 유용합니다.
Sean

7
이것은 주제가 아닌 주제이며, 매우 중요하고 유용한 질문입니다.
Connor

답변:


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. 그것은 나를 위해 잘 작동했습니다. 구문과 지원되는 데코레이터와 같은 cron이 있으면 더 좋을 것입니다 (cryothon 참조하지만 작동하지 않기 때문에이 라이브러리를 사용하지 마십시오. 일정이 제대로 작성되지 않은 것 같습니다).
Tim Ludwinski

23
작업에 매개 변수를 전달하는 방법이 있습니까? 다음과 같은 작업을 수행하고 싶습니다 : schedule.every (). hour.do (job (myParam))
Zen Skunkworx

5
schedule.every (). hour.do (job) 매 시간마다 실행됩니까? 01:00, 02:00, 03:00 등? 시작 시간이 한 시간이 아니더라도?
swateek

1
이 코드가 scheduler.py에 있다고 가정하십시오. 이 코드는 자동으로 실행됩니까?
Kishan

26
@ darrel-holt 및 @ zen-skunkworx :이 do()함수는 전달한 추가 인수를 작업 함수에 전달합니다. schedule.readthedocs.io/en/stable/api.html#schedule.Job.do 예를 들어, 다음과 같이 할 수 있습니다 : schedule.every().hour.do(job, param1, param2)람다를 사용할 필요가 없습니다. 희망이 도움이
되길 바랍니다

65

일반 Python 인수 전달 구문을 사용하여 crontab을 지정할 수 있습니다. 예를 들어, 아래와 같이 Event 클래스를 정의한다고 가정하십시오.

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)

(참고 : 철저히 테스트되지 않았습니다)

그런 다음 CronTab을 일반적인 파이썬 구문으로 다음과 같이 지정할 수 있습니다.

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

이렇게하면 파이썬의 인수 역학을 최대한 활용할 수 있습니다 (위치 및 키워드 인수를 혼합하고 주 및 월 이름에 기호 이름을 사용할 수 있음)

CronTab 클래스는 분 단위로 잠자고 각 이벤트에서 check ()를 호출하는 것으로 정의됩니다. (아마도 일광 절약 시간 / 시간대에주의해야 할 미묘한 부분이 있습니다). 빠른 구현은 다음과 같습니다.

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)

주의해야 할 몇 가지 사항 : 파이썬의 주중 / 월은 0으로 색인화되며 (크론과 달리) 해당 범위는 마지막 요소를 제외하므로 "1-5"와 같은 구문은 range (0,5)가됩니다. 즉 [0,1,2, 3,4]. cron 구문을 선호한다면 구문 분석이 너무 어렵지 않아야합니다.


경험이없는 사용자를 위해 일부 import 문을 추가 할 수 있습니다. 나는 datetime import * from time import sleep 및 time.sleep을 sleep으로 변경하여 모든 클래스를 단일 파일에 넣었습니다. 멋지고 간단한 우아한 솔루션. 감사.
mavnn

1
궁금한 점이 왜 이것이 크로노스보다 선호 되는가? 크로노스가 sched를 사용하기 때문에 버기가 sched입니까? 아니면 구식입니까?
cregox

고마워 Brian, 나는 당신의 솔루션을 생산에 사용하고 꽤 잘 작동합니다. 그러나 다른 사람들이 지적했듯이 실행 코드에는 미묘한 버그가 있습니다. 또한 나는 그것이 요구에 지나치게 복잡하다는 것을 알았습니다.
raph.amiard 2018

1
이것은 시원하지만 여전히 매시간, 분 등을 실행하기 위해 슬래시 표기법을 지원하지 않습니다 ...
Chris Koston

1
우수 아이디어는 이렇게 할 수없는 내가 서버에 sudo를 액세스 할 수없는 경우 예를 들어, 자신의 클래스를 작성합니다 pip install anything:)
Cometsong


27

필자가 찾은 것 중 하나는 파이썬 sched모듈인데, 이것은 당신이 찾고있는 종류 일 수 있습니다.


11
sched는 이제 절대 스케줄링을 수행하는 enterabs ()를 가지고 있습니다.
Jerther

5
sched가 python2 및 3 stdlib의 일부이며 절대 예약을 수행하므로 이것이 선호되는 답변 일 것으로 기대합니다.
Michael


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 ()은 년, 월, 일 등으로 시작합니다.
Trey Stout

9

나열된 솔루션 중 어느 것도 복잡한 cron 일정 문자열을 구문 분석하지 않습니다. 그래서 여기 croniter를 사용하는 내 버전이 있습니다. 기본 요지 :

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있습니까? Atm 저는 "정기적 인 일을하십시오"에 1 분 이상 "* * * * *"을 추가하는 것과 같은 일정을 사용하고 있지만 항상 if 문에 내용이 표시됩니다. 1 분 이상이 걸리면 누락 된 루프 실행을 건너 뛰는 while 루프가 표시됩니다. time.sleepif
TPPZ

@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. 1 분 후 작업을 시작하십시오.

Github의 코드


6

Brian이 제안한 CronTab 클래스 실행 방법에 대한 사소한 수정이 있습니다.

타이밍은 1 초가 지났으며 매 분의 끝에 1 초의 하드 루프로 이어졌습니다.

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

다른 프로세스가 솔루션을 실행하기 위해 파이썬을 시작해야하기 때문에 "순수한 파이썬"방법은 없습니다. 모든 플랫폼에는 프로세스를 시작하고 진행 상황을 모니터링하는 하나 또는 20 가지 방법이 있습니다. 유닉스 플랫폼에서 cron은 오래된 표준입니다. Mac OS X에는 cron과 같은 시작 기능과 워치 독 기능을 결합하여 원하는 경우 프로세스를 살아있게 유지할 수 있습니다. 파이썬이 실행되면 sched 모듈 을 사용하여 작업을 예약 할 수 있습니다.


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

Brian의 솔루션 은 꽤 잘 작동합니다. 그러나 다른 사람들이 지적했듯이 실행 코드에는 미묘한 버그가 있습니다. 또한 나는 그것이 요구에 지나치게 복잡하다는 것을 알았습니다.

누군가가 필요로하는 경우 실행 코드에 대한 더 간단하고 기능적인 대안은 다음과 같습니다.

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
여러 질문에 대한 복사 및 붙여 넣기 상용구 / 언어 답변을 게시 할 때 커뮤니티에서 '스팸 (spammy)'으로 표시되는 경향이 있습니다. 이 작업을 수행하는 경우 일반적으로 질문이 중복됨을 의미하므로 대신 다음과 같이 플래그를 지정하십시오. stackoverflow.com/a/12360556/419
Kev

1

분산 스케줄러를 찾고 있다면 https://github.com/sherinkurian/mani를 확인할 수 있습니다. 재검색이 필요하지만 원하는 것은 아닐 수도 있습니다. (저는 저자입니다) 이것은 둘 이상의 노드에서 클럭을 실행하여 내결함성을 보장하기 위해 만들어졌습니다.


0

그런 것이 이미 존재하는지 모르겠습니다. 시간, 날짜 및 / 또는 달력 모듈을 사용하여 직접 작성하는 것이 쉬울 것입니다. http://docs.python.org/library/time.html을 참조 하십시오.

파이썬 솔루션에 대한 유일한 관심사는 직업의 요구가 실행 항상 될 가능성이 자동으로 재부팅이되는 뭔가 후 "부활"할 수 있다는 점이다 시스템에 의존 솔루션에 의존 할 필요가 있습니다.


3
최상의 코드는 작성하지 않아도되는 코드이지만 자신의 롤은 옵션입니다. 부활이라고 생각합니다.
jamesh


0

서버의 Crontab 방법.

파이썬 파일 이름 hello.py

1 단계 : sh 파일을 만들어 이름을 s.sh로 지정하십시오.

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

2 단계 : Crontab 편집기 열기

크론 탭 -e

3 단계 : 일정 시간 추가

Crontab 형식 사용

2 * * * * sudo sh /home/ubuntu/Shaurya/Folder/s.sh

이 크론은 "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
코드 "print ( 'running backup')"이 5 분 간격으로 1 분 동안 실행되기 때문에 이것은 좋은 생각이 아닙니다. 따라서이 경우 지연 시간은 60 초입니다.
n158

네가 옳아! 지적 해 주셔서 감사합니다.
Duffau
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.