首页
/ Penpot 中的 fd-find 技能:用 fd 在 Penpot 多语言单体仓库中快速精准地查找文件

Penpot 中的 fd-find 技能:用 fd 在 Penpot 多语言单体仓库中快速精准地查找文件

2026-09-05 15:46:40作者:郦嵘贵Just

本文以 Penpot 仓库中 .opencode/skills/fd-find/SKILL.md 这份技能文档为主体,系统讲解 fd(fast file finder)的搜索语法、过滤选项、命令执行占位符与实用场景,并结合 Penpot 仓库的真实目录结构与 .gitignore 配置,说明 fd 如何适配 Clojure、ClojureScript、Rust、TypeScript 混合的大型开源项目。读完后你将掌握在 Penpot 这类单体仓库中按名称、扩展名、时间窗口、文件大小定位文件的完整方法,以及 fd 与 ripgrep、fzf、bat 等工具的组合方式。

一、技能定位:fd 在 Penpot 的 opencode 技能体系中的角色

Penpot 仓库在 .opencode/skills 目录下内置了 16 个供 AI 编码代理(opencode)使用的技能文档,涵盖 ripgrep 文本搜索bat 文件查看、代码审查、提交规范等。fd-find 技能是其中的文件定位技能,与 ripgrep 形成互补分工:fd 负责按路径/文件名快速缩小范围,ripgrep 负责按文件内容搜索。

技能文档通过 YAML frontmatter 声明了运行依赖与安装方式(见 SKILL.md 文件头):

  • requires.bins: ["fd"] —— 使用前需确保系统安装了 fd 可执行文件;
  • 安装方式给出两条路径:Homebrew 安装公式 fdbrew install fd),或 Debian/Ubuntu 系的 apt 包 fd-findapt install fd-find)。
  • description 一句话概括了 fd 的核心卖点:比 find 更友好、默认行为更聪明、且默认遵守 .gitignore

这一点在 Penpot 仓库中尤为重要:仓库根目录的 .gitignore 声明了大量需要跳过的生成物与依赖目录,例如 **/node_modules/frontend/dist//render-wasm/target//exporter/.shadow-cljs/vendor/**/target/backend/target/ 等。fd 默认会读取这些规则并跳过它们,因此搜索源码时天然避开了编译产物与依赖树,无需像 find 那样手动加一堆 -not -path 排除条件。

二、基础搜索:名称匹配与目录参数

技能文档的 Quick Start 部分给出的基础用法是:

# 按名称查找文件(pattern 在文件 basename 上匹配)
fd pattern

# 在指定目录下查找
fd pattern /path/to/dir

# 忽略大小写
fd -i pattern

其中 pattern 默认是正则表达式(这一点与 find -name 的通配符不同),匹配对象是文件名而非完整路径;第三个参数才是被搜索的根目录,缺省为当前目录。

2.1 扩展名与类型过滤

技能文档列出的常用模式:

# 查找所有 Python 文件
fd -e py

# 同时查找多种扩展名(-e 可重复使用)
fd -e py -e js -e ts

# 仅查找目录
fd -t d pattern

# 仅查找普通文件
fd -t f pattern

# 查找符号链接
fd -t l

文档 Tips 一节进一步说明 -t 支持的类型:f(文件)、d(目录)、l(符号链接)、x(可执行文件),并提示 -e-g "*.ext" 写法更简洁。

落到 Penpot 仓库的实际结构上,这些过滤项对应着真实的技术栈切分:

  • fd -e cljc —— 定位 common/src/app 下的 153 个 .cljc 跨平台文件(后端 Clojure 与前端 ClojureScript 共享代码所在);
  • fd -e cljc,clj -t d 之类组合可快速区分 backend/src/app(纯 .clj,约 18 个文件)、frontend/src/app(以 .cljs 为主的 673 个文件);
  • fd -e rs 可切入 render-wasm/src 的 Rust/WASM 渲染引擎源码(50 余个 .rs 文件)。

2.2 智能大小写(smart case)

Performance Tips 一节指出 fd 的智能大小写规则:pattern 全为小写时自动忽略大小写,包含任一大写字母时则区分大小写。这意味着 fd skill 能同时命中 SKILL.mdskill.js,而 fd Skill 只命中大写形式。该行为与 ripgrep 的默认行为一致,也是 fd 相对 find 的"聪明默认值"之一。

三、高级过滤:隐藏文件、gitignore 与搜索深度

# 排除指定模式(-E 可重复)
fd pattern -E "node_modules" -E "*.min.js"

# 包含隐藏文件
fd -H pattern

# 包含被 .gitignore 忽略的文件
fd -I pattern

# 全部搜索(隐藏 + 被忽略)
fd -H -I pattern

# 限制最大深度
fd pattern -d 3

几个关键默认值需要理解:

  1. 默认忽略隐藏文件(文件名以 . 开头的目录与文件):在 Penpot 仓库中,.gitignore.serena/ 记忆系统、node_modules 等都会被默认跳过;
  2. 默认遵守 .gitignore:如前述,Penpot 的 .gitignore/frontend/target//common/coverage/media-processor/dist//test-results/ 等目录列为忽略项,fd 搜索时会自动剔除这些路径,结果只聚焦源码;
  3. -d N 限制深度:例如 fd -t d -d 1 可以一层列出 Penpot 的顶层模块(backend/common/frontend/render-wasm/exporter/media-processor/mcp/plugins/library/),与 AGENTS.md 中描述的模块划分一一对应,是理解仓库结构的快速入口。

四、对搜索结果执行命令:-x 与占位符

技能文档 Execution 小节给出了三个模板:

# 对结果执行命令(批量转换图片格式)
fd -e jpg -x convert {} {.}.png

# 并行执行(fd 默认并行遍历,-x 对每个结果并发执行)
fd -e md -x wc -l

# 配合 xargs(-0 空字节分隔,安全处理特殊字符文件名)
fd -e log -0 | xargs -0 rm

占位符语义在 Tips 一节有完整定义,这是 fd -xfind -exec 的核心差异:

占位符 含义
{} 完整匹配路径
{.} 去掉扩展名后的路径
{/} 文件名(basename)
{//} 所在目录

以 Penpot 仓库为例,fd -e md -x wc -l 会对所有 Markdown 文档(包括 docs/ 下数百个用户指南与技术指南 .md/.njk 文档)并行统计行数;fd -e md -print0 | xargs -0 wc -l 则走管道方式,便于与 awk 等聚合。

五、正则与 glob 模式

# 完整正则匹配(basename 上匹配)
fd '^test.*\.js$'

# 匹配完整路径(--full-path,pattern 针对相对路径匹配)
fd --full-path 'src/.*/test'

# glob 模式(-g,替代 -e/-t 的另一种写法)
fd -g "*.{js,ts}"

注意两者的匹配范围差异:普通 pattern 只在文件basename 上生效,--full-path 则针对相对路径生效——后者适合 Penpot 这类深层嵌套仓库中"所有 src 下的 test 文件"式查询。-g 接受 glob 语法,支持 {js,ts} 花括号扩展,可一次匹配多种扩展名,是 -e py -e js 的等价写法。

六、时间与大小过滤

时间过滤(基于文件的修改/访问时间):

# 最近一天内修改过的文件
fd --changed-within 1d

# 在指定日期之前修改的文件
fd --changed-before 2024-01-01

# 最近一小时内变动的文件
fd --changed-within 1h

大小过滤(支持 k/m/g 量级前缀,+/- 表示大于/小于):

# 大于 10MB 的文件
fd --size +10m

# 小于 1KB 的文件
fd --size -1k

# 大小区间:100k 以上且 10m 以下
fd --size +100k --size -10m

这两组选项在大型仓库的运维排查中价值明显,例如定位异常膨胀的编译产物:fd --size +10m --list-details 可以找出 Penpot 仓库中超出 10MB 的构建输出或媒体资源文件。

七、输出格式控制

# 输出绝对路径
fd --absolute-path

# 详细信息列表(类似 ls -l)
fd --list-details

# 空字节分隔输出(供 xargs -0 使用)
fd -0 pattern

# 强制颜色输出(always/never/auto)
fd --color always pattern

-0--absolute-path 是给下游工具消费时的关键开关:前者保证含空格/换行文件名的安全传递,后者使结果可直接用于跨目录的脚本调用。--list-details 输出权限、大小、修改时间与路径,等价于对搜索结果再执行一次 ls -l

八、技能文档给出的实战用例

Common Use Cases 小节汇总了六个可直接复用的组合命令:

批量删除过期文件(30 天前的普通文件):

fd --changed-before 30d -t f -x rm {}

列出大文件(100MB 以上,带详细信息):

fd --size +100m --list-details

集中拷贝 PDF

fd -e pdf -x cp {} /target/dir/

统计所有 Python 文件总行数

fd -e py -x wc -l | awk '{sum+=$1} END {print sum}'

查找损坏的符号链接

fd -t l -x test -e {} \; -print

限定时间窗口(1~2 天内变更的文件):

fd --changed-within 2d --changed-before 1d

在 Penpot 的 AI 辅助开发流程中,这类"定位 + 批量操作"模式常与 scripts/ 下的仓库工具(如 scripts/check-fmt-clj 格式检查、scripts/gh.py GitHub 助手)衔接:先用 fd 圈定文件集合,再交由项目脚本处理。

九、与 ripgrep / fzf / bat 的工具链集成

技能文档 Integration 小节给出三组经典管道:

# fd 圈定文件集合,rg 搜索内容(fd 自动跳过 node_modules 等忽略目录,rg 减少无关匹配)
fd -e js | xargs rg "pattern"

# fd 提供候选文件,fzf 模糊选择后交给编辑器
vim $(fd -t f | fzf)

# fd 找到 Markdown,bat 带语法高亮批量查看
fd -e md | xargs bat

这三组命令恰好对应 Penpot 技能库的协作关系:fd-find 技能(本文)产出文件列表,ripgrep 技能rg 同样默认遵守 .gitignore)完成内容检索,bat-cat 技能 负责高亮展示结果。三者共享"尊重 gitignore"这一默认契约,在 Penpot 这种生成物众多的仓库中保证了搜索噪音最小化。

十、性能特征与默认行为小结

技能文档 Performance Tips 一节归纳的四条性能事实,也是 fd 相对 find 的核心工程差异:

  1. 速度fd 采用并行遍历(parallel traversal)自动利用多核,通常在大规模目录树上显著快于 find
  2. gitignore 默认生效-I 可关闭;
  3. smart case:全小写 pattern 忽略大小写,含大写则区分;
  4. 正则语义:pattern 默认是正则而非 glob(-g 切换为 glob)。

十一、参数速查表

选项 作用
pattern [dir] 正则匹配 basename,可选根目录
-i 强制忽略大小写
-e ext 按扩展名过滤(可重复)
-t {f,d,l,x} 按类型过滤
-E pattern 排除路径模式
-H 包含隐藏文件
-I 包含 .gitignore 忽略的文件
-d N 限制搜索深度
-x cmd 对每个结果执行命令,{}/{.}/{/}/{//} 占位
--full-path pattern 匹配完整相对路径
-g glob glob 模式匹配
--changed-within / --changed-before 时间窗口过滤
--size ±N{km g} 文件大小过滤
--absolute-path / --list-details / -0 / --color 输出格式控制

fd 的完整选项可通过 man fd 查阅。对于 Penpot 这类同时包含 Clojure 后端、ClojureScript 前端、Rust WASM 渲染引擎与 TypeScript 插件体系的大型仓库,fd 提供的"智能默认值 + 正则语义 + gitignore 感知 + 并行遍历"组合,使按扩展名、深度、时间与大小维度的文件定位成为一条可预测、可组合的日常开发路径。

登录后查看全文
热门项目推荐
相关项目推荐