
find 的 {} + 常被当成「把很多路径合进一次命令」的信号。测试把同一批 28 个文件放进四个子目录,再比较 /usr/bin/find 的 -exec ... {} + 与 -execdir ... {} +,结果并不对称:前者调用一次,后者调用 28 次。
这次只统计启动次数和参数分组,不测耗时。临时目录里有四个子目录,每个放七个文件。helper 每次运行会记录 PID、当前工作目录、参数数量,以及首尾参数;统计时还核对所有调用加起来确实收到 28 个文件参数。
同一棵树的调用与分组
| find 动作 | helper 调用次数 | 工作目录数 | 每次文件参数 | 参数形式 | 文件参数总数 |
|---|---|---|---|---|---|
-exec ... {} + |
1 | 1 | 28 | 绝对路径 | 28 |
-execdir ... {} + |
28 | 4 | 1 | 裸文件名 | 28 |
搜索根目录是绝对临时路径,因而两种动作收到的参数形式可以直接对照:-exec 带完整路径,-execdir 只带当前目录里的文件名。脚本把工作目录、参数和 PID 分栏记录;启动次数由记录行数与唯一 PID 交叉核对,参数数则逐批累加。
-exec 这一行把 28 个匹配路径交给同一次 helper 调用,收到的是绝对路径。换成 -execdir 后,helper 的工作目录落在四个文件目录里,参数变成不带目录部分的文件名;调用次数实际为 28 次,每次只收到一个文件参数。PID 也各不相同,排除了同一进程重复写日志造成的假计数。
结果不支持按「每个目录一批」估算启动数:实际是每文件一次。原因不能只凭这次结果外推到所有 BSD 或 GNU find;文中只写本机版本,别把 -execdir ... {} + 当成跨系统等价的批处理保证。
路径更短,批次却更多
-execdir 的实际用途和 -exec 不完全一样:命令在匹配文件所在目录执行,路径参数通常更短。若任务要求相对当前文件目录操作,短路径有用;但本机这轮测试里,路径变短没有带来更少的 helper 启动。二者测的是不同取舍,不能只看 + 就推断调用次数。
运行脚本会在系统临时目录建树,调用本机 /usr/bin/find,结束后清理目录。它不改博客内容以外的文件,不访问网络;helper 用 PID 及参数记录作断言。完整运行入口:
bash tools/verify-find-exec-batching.sh
脚本也核对了总参数数。否则,只看调用次数可能把漏掉的匹配误当成「少起了进程」。这里两种写法都收到 28 个文件参数,差别确实是分组方式:-exec 一组 28 个,-execdir 二十八组、每组一个。
本机实跑结果来自 tools/verify-find-exec-batching.log,代码块逐字保留:
platform: Darwin 26.2
find: /usr/bin/find
tree: 4 directories, 7 files per directory, 28 files total
-exec ... {} +: invocations=1, working_directories=1, unique_pids=1, file_arguments=28, batch_size_min=28, batch_size_max=28, argument_form=absolute_paths
-execdir ... {} +: invocations=28, working_directories=4, unique_pids=28, file_arguments=28, batch_size_min=1, batch_size_max=1, argument_form=bare_filenames
verification: passed
差异不是路径参数被吞了:两条记录的文件参数总数都是 28。它也不是每个目录一次调用,working_directories=4 与 invocations=28 已把这两种计数分开。调用数来自记录到的 PID 数,参数组大小来自 helper 实收参数,不是 find 打印了几行。
需要批处理时,-exec ... {} + 在这台机器上的表现符合预期;需要在文件所在目录运行时,先在 macOS 目标版本测清 -execdir 的调用粒度。我不会只因为两条命令都写了 + 就假定它们启动次数相同。这个判断会直接影响批量处理耗时,也影响 helper 是否按一组输入设计。