子流程的特定时间会产生不良结果

Specific timing of a subprocess yields undesireable results

我的目标是让此脚本 运行 在远程计算机上,并在 'specific' 的时间,每小时执行一次子进程(一次)。脚本 运行 按预期执行,直到 运行 子进程的时间到了。它不是一次执行一个文件,而是 运行 不停地执行所有文件。我也尝试过使用不同的睡眠时间来打破循环,但无济于事。 这是脚本:

import time
import os
import subprocess

print("Waiting To Upload File ... ")
PATH = r'C:\Users\trcyp\Documents\PythonExample'
while True:
    r = time.localtime()
    m = r.tm_min
    s = r.tm_sec
    if m == 43 and s == 0: #I want to run the script below at this specific min/sec.
        for root, subFolder, files in os.walk(PATH):
            files.sort(key=str.lower)
            subFolder.sort()
            for item in files:
                fileNamePath = os.path.join(root, item)
                print("Uploading: " + fileNamePath)
                print("This is where my subprocess would be")
                #os.remove(fileNamePath)
                print("Upload Successful, " + time.asctime())
                time.sleep(1)
    else:
        print("Current m/s", m, ':', s, "\r", end="")
        time.sleep(1)

这是我的输出:

Waiting To Upload File ... 
Uploading: C:\Users\trcyp\Documents\PythonExample\CustomLoadSheet.pdf
This is where my subprocess would be
Upload Successful, Sun Feb 21 11:43:00 2021
Uploading: C:\Users\trcyp\Documents\PythonExample\Default.aspx.pdf
This is where my subprocess would be
Upload Successful, Sun Feb 21 11:43:01 2021
Uploading: C:\Users\trcyp\Documents\PythonExample\DeliveryDetail(2).pdf
This is where my subprocess would be
Upload Successful, Sun Feb 21 11:43:02 2021
Uploading: C:\Users\trcyp\Documents\PythonExample\DeliveryDetail.pdf
This is where my subprocess would be
Upload Successful, Sun Feb 21 11:43:03 2021
Uploading: C:\Users\trcyp\Documents\PythonExample\ESP8266ModuleV1.pdf
This is where my subprocess would be
Upload Successful, Sun Feb 21 11:43:04 2021
Uploading: C:\Users\trcyp\Documents\PythonExample\track.pdf
This is where my subprocess would be
Upload Successful, Sun Feb 21 11:43:05 2021
Current m/s 43 : 19 

我已经使用了多种配置,但我仍然卡住了。脚本的原始 () 版本运行良好,但使用 time.sleep() 导致执行子流程的时间全天候缓慢移动。我正在尝试一次 运行 一个文件,每次都在同一时间。

按照@VpFB 的建议,您尝试计算等待时间:

import time
import os
import subprocess
import datetime

def get_next(minute, second):
    now = datetime.datetime.now()
    next_time_args = dict(year=now.year, month=now.month, day=now.day,
                          hour=now.hour+1, minute=minute, second=second)
    return datetime.datetime(**next_time_args)

def wait_till_next(next_time):
    now = datetime.datetime()
    seconds = (next_time-now).microseconds / 1e6  # 1e6microsecond=1s
    if seconds>0:
        time.sleep(seconds)



print("Waiting To Upload File ... ")
PATH = r'C:\Users\trcyp\Documents\PythonExample'
next_time = None
waited = False  # this makes sure you upload the next hour's file even if your previous upload took more more than an hour but less than two hours.
while True:
    r = time.localtime()
    m = r.tm_min
    s = r.tm_sec
    next_time = get_next(minute=43, second=0)
    if waited or (m == 43 and s == 0): #I want to run the script below at this specific min/sec.
        for root, subFolder, files in os.walk(PATH):
            files.sort(key=str.lower)
            subFolder.sort()
            for item in files:
                fileNamePath = os.path.join(root, item)
                print("Uploading: " + fileNamePath)
                print("This is where my subprocess would be")
                #os.remove(fileNamePath)
                print("Upload Successful, " + time.asctime())
                time.sleep(1)
    else:
        print("Current m/s", m, ':', s, "\r", end="")
        wait_till_next(next_time)
        waited = True

apscheduler 基本上在 python 脚本中为您提供 cron。以下是您的脚本示例,运行在小时后 43 分钟执行计划任务:

import apscheduler
import os
from datetime import datetime
import time
import subprocess
from apscheduler.schedulers.background import BackgroundScheduler


def make_generator_func_callable(gen_func):
    # a decorator that makes generators callable so they can be used with scheduler
    task_generator = gen_func()
    def callable_generator():
        try:
            return next(task_generator)
        except StopIteration:
            print("(nothing left to do)")
    return callable_generator


@make_generator_func_callable
def my_task(path=PATH):

    for root, subFolder, files in os.walk(path):
        files.sort(key=str.lower)
        subFolder.sort()
        for item in files:
            fileNamePath = os.path.join(root, item)
            print("Uploading: " + fileNamePath)
            print("This is where my subprocess would be")
            #os.remove(fileNamePath)
            print("Upload Successful, " + time.asctime())
            #time.sleep(1)
            yield


@make_generator_func_callable
def test_task():
    for item in [1,2,3]:
        print(datetime.now())
        print(item)
        yield


if __name__ == "__main__":

    print("Waiting To Upload File ... ")
    # start scheduler as a background/non-blocking process
    scheduler = BackgroundScheduler()
    scheduler.start()

    # your cron-style task. This is also an "interval" option
    # scheduler.add_job(my_task, "cron", minute="43", id='my_job')
    scheduler.add_job(test_task, "interval", seconds=3, id='test_job')

    # this is a blocking prompt to prevent your script from exiting
    ans = input("press return to shutdown the script")
    scheduler.shutdown()

为什么要使用专用的调度程序包?安排任务并不完全是微不足道的,并且从专用的后台进程中受益匪浅。如果您尝试“自己动手”,您可能 运行 遇到以下任何问题:

  • 作业因完成速度快而意外提交多次
  • jobs missed/skipped 因为您的进程在需要的时间忙于做其他事情
  • 作业失败并阻止您的代码继续

相比之下,apscheduler 为您解决所有这些困难,并在您需要时提供更高级的功能(跨会话的作业持久性等)

请注意,修改后的脚本会将您的脚本转换为生成器函数(然后将其包装为可调用)。这可能不是最优雅的解决方案,但可以通过最少的修改完成工作

正如评论中所讨论的那样,这里是一个睡眠时间计算示例,该任务在给定的分钟和秒内每小时重复一次。

import datetime as dt

def get_sleeptime(minute, second):
    """Return seconds from now till nearest *:min:sec time"""
    now = dt.datetime.now()
    sched = now.replace(minute=minute, second=second, microsecond=0)
    sleeptime = (sched - now).total_seconds() # the difference is a timedelta object
    if sleeptime < 0:
        sleeptime += 3600
    return sleeptime
    
print(get_sleeptime(43,0) # test

我认为它在主循环中的用法很清楚。

好的。因此,在研究和测试所有建议和答案之后,@VPfB 的最新答案最容易集成到我现有的脚本中。我只是想 post 在这里供其他希望使用它或做类似事情的人使用。我唯一需要做的是在结束时放置 1 秒的睡眠,但在 get_sleeptime 调用之前打破循环足够长的时间让计时器接管。到目前为止它运行完美。这是最终产品...

import datetime as dt
import time
import os
import subprocess

PATH = '/home/pi/Public/'
def get_sleeptime(minute, second):
    now = dt.datetime.now()
    sched = now.replace(minute=minute, second=second)
    sleeptime = (sched - now).total_seconds() # the difference is a timedelta object
    if sleeptime < 0:
        sleeptime += 3600
    return sleeptime

for root, subFolder, files in os.walk(PATH):
    files.sort(key=str.lower)
    subFolder.sort()
    for item in files:
        fileNamePath = os.path.join(root, item)
        print("Uploading: " + fileNamePath)
        print("")
        subprocess.run(['telegram-upload', '-d', '-f', 'file_dumpster', str(fileNamePath)])
        print("")
        print("Upload Successful >> " + time.asctime())
        print("")
        time.sleep(1) #added to break loop long just long enough
        print(" <<... Waiting To Upload New File ...>> ")
        time.sleep(get_sleeptime(11, 0)) #set at 11 minutes past the hour

这是输出...

Uploading: C:\Users\trcyp\Documents\PythonExample\New Text Document - Copy (2).txt
This is where my subprocess would be
Upload Successful >> Mon Feb 22 19:10:26 2021  #initial file upload
 <<... Waiting To Upload New File ...>> 
Uploading: C:\Users\trcyp\Documents\PythonExample\New Text Document - Copy (3).txt
This is where my subprocess would be
Upload Successful >> Mon Feb 22 19:11:00 2021  #11 minutes past the hour
 <<... Waiting To Upload New File ...>> 
Uploading: C:\Users\trcyp\Documents\PythonExample\New Text Document - Copy (4).txt
This is where my subprocess would be
Upload Successful >> Mon Feb 22 20:11:00 2021  #11 minutes past the hour
 <<... Waiting To Upload New File ...>>