树莓派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规则实现插入触发:
- 把现有逻辑修改为单次执行的脚本(删除while循环部分,启动后直接处理U盘、发送邮件后退出)
- 新增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"
- 执行
udevadm control --reload-rules生效规则,之后U盘插入时系统会自动触发脚本执行,天生保证单次触发,不会重复执行。
内容的提问来源于stack exchange,提问作者DanDaMan
相关产品推荐
相关产品推荐

