一段 Python 脚本,文件丢进去就自动归位

从相机导出的照片、从压缩包里解出的图片、游戏机传过来的截图,最后往往全都躺在同一个文件夹里,几百个文件挤成一团。手动整理这件事,多数人坚持不过三天。系统自带的自动整理规则又太死板,想按文件名里的日期分层建目录,基本无从下手。

用 Python 搭一个「落地文件夹」是更灵活的解法:指定一个目录,任何文件丢进去,脚本立刻识别、改名、按规则挪到该去的位置。关键在于不用笨办法。最原始的思路是写个死循环反复列目录、比对差异,既费电又不及时。Watchdog 这个库直接调用操作系统的原生接口——Linux 上是 inotify,macOS 上是 FSEvents——由系统在文件变化时主动通知程序。

一、先把环境搭好

How-To Geek 撰稿人 Bobby Jack 强调,虚拟环境这一步别省,否则后面会后悔:

mkdir my-project
cd my-project
python3 -m venv venv
source venv/bin/activate

Windows 下激活命令换成 venv\Scripts\activate。然后安装库:

python -m pip install watchdog

二、跑通最小示例

先别管细节,把下面这段跑起来,确认监听有效:

import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    event_handler = LoggingEventHandler()

    observer = Observer()
    observer.schedule(event_handler, '.', recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()

    observer.join()

运行后它会盯住当前目录。此时新建一个空文件,或者把某个文件改名,控制台就会打印出对应的事件日志。

三、按文件名里的日期自动分层

Bobby Jack 的实际需求是整理游戏机截图。这类文件名形如 2025010409204300-D796833F72011BDFC9868896D061B51F.jpg,前 14 位依次是年、月、日、时、分、秒,接着两位用于区分同一秒内的多张,短横线后是 32 位十六进制的软件标识,扩展名固定为 .jpg(同一批里的 .mp4 视频直接忽略)。目标是把它们整理成「年/月/日」的目录结构。

先定义好三个常量和一条正则表达式,括号里的捕获组分别对应年、月、日和剩余部分:

import os
import time
import re
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

WATCH_FOLDER = Path("/tmp")
TARGET_FOLDER = Path("/Users/bobby/Pictures/switch-screenshots")
PATTERN = r"^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{8}-[A-F0-9]{32}\.jpg)$"

处理逻辑写在事件处理类里,覆盖父类的 on_created 方法:

class FileDropHandler(FileSystemEventHandler):
    def on_created(self, event):
        if event.is_directory:
            return

        file_path = Path(event.src_path)

        if not re.match(PATTERN, file_path.name):
            return

        self._wait_for_file_copy(file_path)
        self.process_file(file_path)

    def process_file(self, file_path):
        try:
            search = re.search(PATTERN, file_path.name)
            if not search:
                return

            target = TARGET_FOLDER / search.group(1)
            target = target / search.group(2)
            target = target / search.group(3)

            target.mkdir(parents=True, exist_ok=True)
            file_path.move(target / search.group(4))

            print(f"Moved: {file_path.name}")
        except Exception as e:
            print(f"Error processing {file_path.name}: {e}")

其中 _wait_for_file_copy 是一段用于确认文件已经复制完毕的检查代码,作用是避免在文件还没写完时就去搬动它。主程序部分与前面的示例几乎一致,只是把处理器换成 FileDropHandler(),并把监听路径指向 WATCH_FOLDER

四、改成自己的规则

监听路径和目标路径目前是写死的,做成命令行参数会更好用。真正需要动的只有那条正则表达式:把它换成自己文件的命名规律,捕获组对应想要的目录层级,整套逻辑就能直接复用。

能识别落地文件并对其做处理之后,可发挥的空间就大了——合并零散笔记、按内容把文档分类、批量把图片缩放到指定尺寸,都只是在这个骨架上多写几行的事。

来源:How-To Geek