python 3.12 本身不提供跨平台剪贴板历史功能,pyperclip 仅支持读写当前内容;实现历史记录需按平台分别采用 winapi 消息钩子、macos nspasteboard changecount 轮询或 linux xclip/wl-paste 定期比对,并用 sqlite 存储去重。

Python 3.12 本身不提供跨平台剪贴板历史功能,pyperclip 只能读写当前剪贴板内容,无法监听或记录历史;真要实现历史记录,必须结合系统级剪贴板监听机制,且各平台方案完全不同。
Windows 上用 win32clipboard + 消息钩子监听剪贴板变化
Windows 剪贴板支持 WM_DRAWCLIPBOARD 消息广播,但 Python 标准库无法直接注册钩子,需借助 pywin32 或 ctypes 调用 WinAPI。注意:仅靠轮询 OpenClipboard/GetClipboardData 效率低、易漏事件、还可能阻塞其他应用。
- 必须用
SetClipboardViewer注册窗口句柄,并在消息循环中处理WM_DRAWCLIPBOARD -
pywin32的win32gui.PumpWaitingMessages()不适合长期后台运行,推荐用ctypes自建最小窗口类(避免依赖 GUI 框架) - 每次获取内容前务必检查格式:
IsClipboardFormatAvailable(CF_UNICODETEXT),否则读取失败会抛pywintypes.error - 文本之外的内容(如图片、HTML)需额外判断
CF_DIB或CF_HTML,且二进制数据需截断或哈希存储,避免内存暴涨
macOS 上用 NSPasteboard 监听 changeCount 变化
macOS 没有广播消息机制,只能轮询 NSPasteboard.generalPasteboard().changeCount,但轮询间隔不能低于 0.2 秒,否则触发 NSPasteboard 内部限流,导致后续读取返回旧值。
- 必须用
AppKit.NSPasteboard(来自pyobjc),pyperclip在 macOS 下无法获取 changeCount - 每次读取前先比对当前
changeCount和上次缓存值,仅当增长时才调用pasteboard.stringForType_(NSPasteboardTypeString) - 若用户复制了图片,
stringForType_返回None,需 fallback 到dataForType_(NSPasteboardTypeTIFF)并计算hashlib.sha256(data).hexdigest()[:8]存摘要 - 脚本需声明为「辅助功能」权限,否则首次运行时会被系统拦截——需手动在「系统设置 > 隐私与安全性 > 辅助功能」中添加 Python 解释器
Linux 上依赖 xclip 或 wl-clipboard,但无法可靠监听
X11 和 Wayland 均无标准剪贴板变更通知机制;pyperclip 底层调用的 xclip 是命令行工具,无事件能力。唯一可行路径是定期执行命令并比对输出,但存在固有缺陷:
- X11 下用
subprocess.run(['xclip', '-o', '-selection', 'clipboard'], text=True, timeout=0.5),超时必须设小(否则卡死),失败时捕获subprocess.TimeoutExpired或subprocess.CalledProcessError - Wayland 下必须用
wl-paste --no-newline,且要求用户已安装wl-clipboard(Ubuntu 默认不装,Debian 需apt install wl-clipboard) - 无法区分「内容未变」和「读取失败」,建议加双缓冲:连续两次读取相同内容才记为有效更新
- 不要尝试用
inotify监控/dev/input/设备节点——权限高、无剪贴板语义、且 Ctrl+V 不一定触发设备事件
历史存储与去重必须用 SQLite 而非 JSON 文件
用文件存历史看似简单,但多进程写入、编码冲突、锁管理会迅速失控;SQLite 支持 WAL 模式和行级锁,适合后台常驻进程高频写入。
- 表结构至少含:
id INTEGER PRIMARY KEY,content TEXT,type TEXT(如text/plain、image/png),created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - 插入前执行
INSERT OR IGNORE INTO history (content, type) VALUES (?, ?),利用UNIQUE(content, type)约束去重——纯文本内容可先strip()再哈希,避免空格差异误判 - 查询最近 100 条用
SELECT * FROM history ORDER BY created_at DESC LIMIT 100,别用ORDER BY id DESC,因 SQLite 的id不严格对应时间顺序 - 每日自动清理超过 7 天的记录:
DELETE FROM history WHERE created_at ,放在每次启动时执行一次即可
跨平台剪贴板历史真正的难点不在代码,而在平台权限模型和事件语义差异:Windows 需窗口消息循环,macOS 需辅助功能授权,Linux 则根本没事件——所有「监听」本质都是妥协后的轮询。别试图用一个抽象层掩盖这些差异,每个平台的初始化、错误恢复、资源释放逻辑都得单独写清楚。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











