跳转到内容

文件监听与批处理 ​

「下载完成就自动整理」「截图一存就自动压缩」这类需求有两个部分:怎么知道文件变了,和变了之后安全地批量处理。

第二部分才是风险所在。批处理脚本出错时不会只错一个文件,它会一口气错几百个。所以本文的核心方法是:任何批量操作,先跑一遍只打印不执行的版本。

开始之前 ​

需要读过用 launchd 让任务按时间和事件运行,本文的监听方案基于它。全部练习在可丢弃的目录里进行。

三种监听方式 ​

方式适合代价
launchd WatchPaths少数几个固定目录系统原生,无需额外软件
文件夹操作(Folder Actions)图形化流程,接快捷指令只能盯单个文件夹,调试信息少
fswatch需要递归监听、过滤、去抖需额外安装,自己管理进程

launchd 监听目录 ​

在 plist 里把 StartInterval 换成 WatchPaths:

xml
<key>WatchPaths</key>
<array>
    <string>/Users/你的用户名/Downloads</string>
</array>

WatchPaths 接收一个路径数组,其中任一路径发生变化时触发任务。注意两个限制:

  • 必须写绝对路径,~ 不会被展开。
  • 它监听的是该目录本身的变化,不递归进子目录。

另一个字段 QueueDirectories 行为不同:目录非空时才触发,并且会持续触发直到目录被清空。适合「放进来就处理掉」的队列模式,不适合只想知道「有变化」的场景。

文件夹操作 ​

访达里右键点目录 →「服务 → 文件夹操作设置」,可以把一个自动操作或快捷指令绑定到该文件夹。好处是全图形化;坏处是出错时几乎没有可查的信息。适合流程简单、失败了也无所谓的场景。

fswatch ​

sh
brew install fswatch
fswatch -o ~/Downloads | while read -r _; do echo "检测到变化"; done

-o 让它只输出变化事件的数量而不是路径列表,适合配合循环使用。while read -r _ 每收到一个事件执行一次循环体,-r 表示不转义反斜杠,_ 是个约定俗成的「不使用这个变量」的名字。

fswatch 默认递归监听,并且支持 --exclude 过滤。缺点是你要自己负责让它常驻——通常还是交给 launchd 管理。

去抖是必须考虑的。 复制一个大文件时,系统会在写入过程中多次报告变化。脚本若立刻处理,会读到一个尚未写完的文件。常见做法是触发后先等几秒,或者比较文件大小两次是否一致再动手。

批处理:先演练,再执行 ​

这是本文最该记住的部分。

用 find 之前先看它选中了什么 ​

sh
find ~/Desktop -name "*.png" -mtime +30 -print

find 从指定目录开始查找,-name "*.png" 匹配文件名,-mtime +30 表示修改时间在 30 天以前,-print 打印结果。

先只用 -print 看清单,确认这些正是你想处理的文件。确认之后再换成 -exec 或 -delete。顺序反过来做,你会在看到清单的同时文件已经没了。

rsync 的演练参数 ​

sh
rsync -avn ~/source/ ~/destination/

-a 保留权限和时间戳,-v 显示处理的文件,-n 表示只演练不实际执行。输出即将发生的操作,确认无误后去掉 n 再跑。

路径末尾的斜杠有讲究:~/source/ 表示复制目录里的内容,~/source 表示复制目录本身。这个差别会导致目标里多一层嵌套,演练时正好能看出来。

批量重命名 ​

sh
for f in *.jpeg; do echo mv -- "$f" "${f%.jpeg}.jpg"; done

for f in *.jpeg 遍历当前目录下所有 .jpeg 文件,${f%.jpeg} 去掉结尾的 .jpeg,-- 表示后面的参数不再当作选项(文件名以连字符开头时靠它避免出错)。

注意 mv 前面的 echo:这一版只打印将要执行的命令,不真的改名。看完输出确认无误,删掉 echo 再跑一次。这个「先加 echo」的习惯适用于任何批量命令。

三条硬性约束 ​

  1. 不要在原目录上做第一次实验。 复制一份到 /tmp 下的测试目录。
  2. 不要用 rm -rf 配合变量。 变量为空时 rm -rf "$dir"/* 的行为可能远超预期。
  3. 处理不可重建的资料前先确认备份。 照片图库、文稿这类内容,见备份与恢复。

权限会挡住你 ​

脚本访问桌面、文稿、下载这几个目录时会触发隐私授权。从 launchd 运行时没有人点「允许」,任务会静默失败。

先在终端手工执行一次,完成授权弹窗,再交给后台。机制见TCC 隐私授权。

检查结果 ​

建一个监听任务,确认三件事:往被监听目录里放一个文件会触发;任务的错误输出文件是空的;launchctl print 的退出码是 0。

三项里缺任何一项都不算成功——尤其是第二、三项,没有它们你无法区分「任务没触发」和「任务触发了但失败了」。

动手练习 ​

在一个完全可丢弃的目录里完成一轮:

  1. mkdir -p /tmp/watch-test && cd /tmp/watch-test,用 touch a.jpeg b.jpeg c.jpeg 造三个空文件。
  2. 执行带 echo 的重命名循环,读懂输出的三条命令。
  3. 去掉 echo 真正执行,用 ls 确认改名结果。
  4. 执行 find /tmp/watch-test -name "*.jpg" -print,确认它选中的正是这三个。
  5. rm -rf /tmp/watch-test 清理。

成功标准是第 2 步你能在执行前就说出会发生什么。养成这个习惯之后,批处理脚本的风险会下降一个数量级——出错的那一次,你在 echo 阶段就发现了。

返回自动化与脚本