Penpot 中的 fd-find 技能:用 fd 在 Penpot 多语言单体仓库中快速精准地查找文件
本文以 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 安装公式
fd(brew install fd),或 Debian/Ubuntu 系的 apt 包fd-find(apt 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.md 与 skill.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
几个关键默认值需要理解:
- 默认忽略隐藏文件(文件名以
.开头的目录与文件):在 Penpot 仓库中,.gitignore、.serena/记忆系统、node_modules等都会被默认跳过; - 默认遵守
.gitignore:如前述,Penpot 的 .gitignore 把/frontend/target/、/common/coverage、/media-processor/dist/、/test-results/等目录列为忽略项,fd搜索时会自动剔除这些路径,结果只聚焦源码; -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 -x 与 find -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 的核心工程差异:
- 速度:
fd采用并行遍历(parallel traversal)自动利用多核,通常在大规模目录树上显著快于find; - gitignore 默认生效:
-I可关闭; - smart case:全小写 pattern 忽略大小写,含大写则区分;
- 正则语义: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 感知 + 并行遍历"组合,使按扩展名、深度、时间与大小维度的文件定位成为一条可预测、可组合的日常开发路径。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00