上一篇把 fzf 定位成通用选择器本篇补齐高质量候选的生产端。ripgreprg搜索内容fd 搜索路径find 提供最完整的 POSIX/文件属性表达三者不是互相淘汰而是覆盖不同精度与可移植性需求。一、痛点搜索结果多不代表搜索正确rg pattern默认递归、尊重.gitignore、跳过隐藏和二进制文件这正适合代码仓库却可能漏掉被忽略的生成物。-u逐级放宽过滤-uu包含隐藏与忽略文件-uuu还尝试二进制排障时要明确自己在哪一层。--hidden只包含隐藏文件仍尊重忽略规则。搜索秘密或构建缓存前也要评估输出是否会进入日志。fd 以用户友好的默认值搜索文件名模式默认是正则而非 shell glob精确文件名可用--glob或--fixed-strings。find 虽语法冗长却能按权限、时间、所有者和文件系统边界筛选并用-exec command {} 安全批量执行。不要解析ls它是面向人的展示格式。二、原理正则引擎、忽略层和退出码ripgrep 默认使用 Rust regex 引擎保证线性时间但不支持回溯和环视确需这些特性可用-P启用 PCRE2代价是可用性与性能特征变化。字面搜索用-F单词边界用-w文件类型用-t上下文用-C。先缩小文件集合再写复杂模式通常比一个巨型正则更容易验证。退出码 0 表示找到匹配1 表示没有匹配2 表示错误。开启set -e的脚本若把“没找到”当失败会提前退出应用显式分支处理。机器消费时用--json、-0或--null-data不要解析带颜色、列宽和本地化的人类输出。搜索工具的退出码也属于接口。下面程序构造独立夹具模拟“忽略目录、按后缀筛选、逐行报告”三步。它让没有 rg 的环境也能验证搜索边界迁移到 rg 时应保持输入范围、模式与退出语义不变而不是只替换命令名。frompathlibimportPathfromtempfileimportTemporaryDirectorywithTemporaryDirectory()astemp:rootPath(temp)files{src/app.py:token load_secret()\nprint(ready)\n,src/test_app.py:def test_ready():\n assert True\n,vendor/old.py:token ignored\n,README.md:token appears in docs\n,}forrelative,contentinfiles.items():pathroot/relative path.parent.mkdir(parentsTrue,exist_okTrue)path.write_text(content,encodingutf-8)matches:list[str][]forpathinsorted(root.rglob(*.py)):relativepath.relative_to(root)ifvendorinrelative.parts:continuelinespath.read_text(encodingutf-8).splitlines()fornumber,lineinenumerate(lines,1):iftokeninline:matches.append(f{relative}:{number}:{line.strip()})formatchinmatches:print(match)print(fmatches{len(matches)})print(fexit_code{0ifmatcheselse1})运行输出src/app.py:1:token load_secret() matches1 exit_code0三、实现可复用的仓库审计下面脚本同时检查调试语句、疑似私钥头和超大文件报告可复现且不修改仓库。模式只是初筛命中后仍需人工判断安全扫描不能只依赖几个正则。#!/usr/bin/env bashset-euopipefailroot${1:-.}root$(cd$rootpwd-P)command-vrg/dev/null21||{echo需要 ripgrep2;exit1;}fail0run_check(){locallabel$1pattern$2shift2printf\n[%s]\n$labelsete rg --line-number--column--colornever$$pattern$rootstatus$?set-ecase$statusin0)fail1;printf发现候选项请复核\n;;1)printf未发现\n;;*)printf搜索失败退出码%s\n$status2;exit$status;;esac}run_checkdebug statements\console\.log\(|debugger;|breakpoint\(\)\-g!vendor/**-g!dist/**run_checkprivate key headers\BEGIN (RSA |OPENSSH |EC )?PRIVATE KEY\--hidden-g!.git/**printf\n[large files]\nfind$root-typef-size5M-not-path*/.git/*-printprintf\nresult%s\n$failexit$fail若希望把结果交给上一篇的 fzf可让rg --line-number --no-heading输出path:line:textfzf 用--delimiter : --preview sed -n {2}p {1}预览。更严格的机器接口使用 JSON再由 jq 解析字段以免 Windows 盘符或文本中的冒号破坏切分。patternDEPRECATED|REMOVE_BEFORE_RELEASEsete rg--json$pattern.${TMPDIR:-/tmp}/rg-result.jsonlstatus$?set-eif[[$status-gt1]];thenprintfripgrep failed: %s\n$status2exit$statusfiprintfmatch_recordsgrep-ctype:match${TMPDIR:-/tmp}/rg-result.jsonl||true四、踩坑忽略规则与批量修改.gitignore、父目录 ignore、全局 Git ignore 和.ignore会共同影响结果。用rg --debug可观察过滤原因但输出很大应用小目录复现。符号链接默认不跟随启用-L后可能跨出仓库或形成环find 可用-xdev限制文件系统边界。搜索压缩包需要额外预处理不能把“没输出”误判为内容安全。把搜索直接接sed -i或删除命令风险很高。先输出 NUL 分隔清单人工审阅再用能正确处理 NUL 的循环执行对每个文件保留版本控制或备份。并行执行时还要考虑多个进程写同一文件。性能比较应保持相同的隐藏、忽略、编码和正则语义否则数字没有可比性。五、验证用刻意构造的小夹具测边界建立含普通文件、隐藏文件、被忽略文件、空格路径、二进制字节和符号链接的临时目录逐项验证过滤层。再验证匹配、无匹配、无权限三种退出路径。团队可以把关键搜索写进 CI但必须锁定工具最低版本并让误报能通过明确的 ignore 文件解释。至此我们有了快速、可组合的查找层。下一篇会把反复使用的搜索和导航流程封装成别名与 Shell 函数重点区分何时用别名、何时必须写函数或独立脚本。参考来源ripgrep User Guidefd 官方文档GNU findutils Manual 觉得有用就点个赞 收藏方便回头查阅有疑问直接在评论区留言我看到都会回。 本文属于《终端与命令行进阶》系列持续更新关注不迷路。 文章里的代码都能直接跑。想要可直接 clone 的完整工程 配套部署脚本 / 踩坑清单评论一声或发邮件到cj2664qq.com我免费发你。如果你正好在做类似系统、或有工程化难题想找人做也欢迎邮件聊一句——我按实际情况评估能落地的就接单或出方案。评论和邮件都能直接找到我不用跳别的平台。