You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派USB自动拷贝发邮件脚本优化:实现单次插入触发后空闲等待

问题核心原因

你现有脚本的重复执行问题来自两个核心缺陷:

  • 没有对已处理过的U盘挂载点做标记,每1秒的轮询只要检测到挂载存在就会重复执行任务
  • 时间变量在脚本启动时就固定了,后续所有日志的时间都不会更新
  • driver、opts 用全局变量传递,逻辑混乱易出错
最小改动修复方案(适配现有代码逻辑)

只需要新增已处理挂载点的校验逻辑,无需调整原有功能函数的核心实现,修改后代码如下:

import smtplib, ssl
import shutil
import os
from time import sleep
from distutils import dir_util
from psutil import disk_partitions
from datetime import datetime
from datetime import date
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication

# 存储已处理过的挂载点
processed_mounts = set()

def CopyFilesFromSD(driver, opts):
    # 时间获取移到函数内部,保证每次执行取当前时间
    now = datetime.now()
    current_time = now.strftime("%H:%M:%S")
    today = date.today()
    day = today.strftime("%B %d, %Y")
    try:
        dir_util.copy_tree(driver+'/REDACTED', '/home/pi/Desktop/Transferred Files')
        print("Files Succesfully Copied")
        if 'rw' in opts:
            with open(driver+'/Transfer_Log_REDACTED.txt', 'a', encoding = 'utf-8') as fp:
                fp.write("\nFiles Succesfully Transferred to REDACTED on " + day + " at " + current_time)
    except Exception as e:
        print(f"Device unmounted during transfer process: {str(e)}")
    
def SendFiles():
    dir_path = "/home/pi/Desktop/Transferred Files/"
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)
    files = os.listdir(dir_path)
    if not files:
        return

    msg = MIMEMultipart()
    msg['To'] = "REDACTED"
    msg['From'] = "REDACTED"
    msg['Subject'] = "REDACTED"
    password = "REDACTED"

    body = MIMEText('Test.', 'html', 'utf-8')
    msg.attach(body)

    for f in files:
        file_path = os.path.join(dir_path, f)
        attachment = MIMEApplication(open(file_path, "rb").read(), _subtype="txt")
        attachment.add_header('Content-Disposition','attachment', filename=f)
        msg.attach(attachment)

    context = ssl.create_default_context()
    with smtplib.SMTP_SSL("REDACTED", 465, context=context) as server:
        server.login(msg['From'], password)
        server.sendmail(
            msg['From'], msg['To'], msg.as_string()
        )
    shutil.rmtree(dir_path, ignore_errors = False)

while True:
    sleep(1)
    # 先获取当前所有符合条件的挂载点
    current_valid_mounts = set()
    for item in disk_partitions():
        if 'nodev' in item.opts:
            current_valid_mounts.add(item.mountpoint)
            # 只处理新出现的、未处理过的挂载点
            if item.mountpoint not in processed_mounts:
                processed_mounts.add(item.mountpoint)
                CopyFilesFromSD(item.mountpoint, item.opts)
                SendFiles()
    # 清理已经卸载的挂载点记录,下次插入可以重新触发
    processed_mounts = processed_mounts.intersection(current_valid_mounts)
更稳定的优化方案(替代轮询)

如果要彻底抛弃while True轮询的不稳定逻辑,可以用Linux原生的udev规则实现插入触发:

  1. 把现有逻辑修改为单次执行的脚本(删除while循环部分,启动后直接处理U盘、发送邮件后退出)
  2. 新增udev规则文件/etc/udev/rules.d/99-usb-trigger.rules,写入如下规则:
ACTION=="add", KERNEL=="sd[a-z][0-9]", SUBSYSTEMS=="usb", RUN+="/usr/bin/python3 /path/to/your/script.py"
  1. 执行udevadm control --reload-rules生效规则,之后U盘插入时系统会自动触发脚本执行,天生保证单次触发,不会重复执行。

内容的提问来源于stack exchange,提问作者DanDaMan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 17:30:03