返回资讯中心
外部精选
软件工程
#开发者实时流

pdfq:兼容 pdfgrep 的快速并行 PDF 搜索工具,用 Rust 重写

来源标题:Pdfq – Fast parallel PDF search, drop-in for pdfgrep (Rust)

开发者发布 pdfq,一个用 Rust 从零重写的 PDF 搜索工具,命令行与 pdfgrep 兼容,可作为其替代品。它用 pdf-extract 逐页提取文本,用 regex crate 匹配,并通过 rayon 多核并行搜索页面,支持 -i、-n、-c、-P、-r 等 pdfgrep 常用参数。输出格式、参数和 GNU-grep 风格退出码均沿用 pdfgrep,另提供可选的页面文本磁盘缓存。

Hacker News 最新xyzhaku2026/11/9 · 今天7 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

快速、并行的 PDF 搜索工具,命令行与 pdfgrep 兼容。

pdfq 是用 Rust 从零重写的实现。它使用 pdf-extract 逐页提取文本,用 regex crate(或在 -P 模式下用 fancy-regex)进行匹配,并通过 rayon 在所有核心上并行搜索页面。输出格式、命令行标志和退出码都遵循 pdfgrep,因此可以直接作为替代品使用。

兼容 pdfgrep 的标志:-i、-n、-c、-o、-l、-L、-q、-F、-P、-A/-B/-C、-m、-r、-R、--color、--page-range 等

  • 并行的页面与文件处理(rayon)
  • 现代正则引擎:默认使用 Rust regex,-P 模式使用 fancy-regex
  • 可选的磁盘缓存,保存已提取的页面文本(--cache)
  • GNU-grep 风格的退出码:0 表示匹配,1 表示无匹配,2 表示错误
  • 彩色输出,遵循 GREP_COLORS(mt/ms/mc、fn、ln、se)

构建

cargo build --release

生成的二进制文件位于 target/release/pdfq。

pdfq [OPTION]... PATTERN FILE...

示例:

pdfq -n “error” report.pdf pdfq -r -i “todo” ./docs pdfq -c -e foo -e bar a.pdf b.pdf pdfq -n -C2 --page-range 3-5 “summary” thesis.pdf pdfq -l -F “exact string” .

运行 pdfq --help 查看完整选项列表。

代码 含义
0 至少有一处匹配
1 无匹配
2 错误(用法错误、文件无法读取等)

使用 -q 时,匹配优先于错误:只要有任何匹配就立即以 0 退出。

递归遍历目录(-r)时的错误会输出到 stderr,但不会改变退出码,这一点与 pdfgrep 一致。

[filename<sep>][pagenum<sep>]text

sep 在匹配行上是 :,在上下文行上是 -。

  • -n 打印页码(pdfgrep 没有行号,PDF 是按页的)
  • -Z 将文件名分隔符替换为 NUL
  • 上下文组之间用 -- 分隔,但仅在确实跳过了行时才输出

与 pdfgrep 的差异

  • 文本提取使用 pdf-extract(纯 Rust)而非 poppler,因此在复杂 PDF 上版面重建可能略有不同
  • 模式中的 . 不匹配换行(Rust regex 语义;POSIX 的 . 会匹配)。多行锚点:^ 只匹配页面文本的开头,$ 只匹配结尾
  • 递归遍历的输出按名称排序以保证结果可复现;pdfgrep 按原始 readdir 顺序打印
  • --help 由 clap 生成,列出所有选项,而非 pdfgrep 简短的“常用”列表
  • 不支持 --unac 重音折叠(该选项会被拒绝并给出明确错误)

测试

单元测试和集成测试(包含针对加密 PDF 密码检查与 --cache 的回归测试套件):

cargo test

针对参考 pdfgrep 二进制文件的差分测试(比较 stdout、stderr 和退出码;程序名已归一化):

cargo build --release PDFGREP=/path/to/pdfgrep bash tests/difftest.sh

如果未设置 PDFGREP,脚本会使用 PATH 中的 pdfgrep,若找不到则以 2 退出。

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →