
我最早开始真正研究 grep 的替代工具是某次在几个 GB 的仓库里跑grep -rn等结果等到咖啡都凉了的时候。那个仓库有几十个目录、上万份代码文件、海量日志和静态资源一条简单的文本匹配居然要扫十几秒。换用rgripgrep之后第一次运行基本在 1 秒内出结果。那一瞬间我就明白不是老命令不行了是我们手里早就有了更顺手的家伙只是很多人还没切换过来。这次想写一篇关于 grep 工具家族的大盘点rg、tgrep、rawgrep、zg。它们解决的是不同层次的问题有代码搜索、有结构化文本检索、有原样匹配需求还有压缩文件里的搜索。通读一遍你至少能学会两件事一是知道什么场景该用哪个工具二是能从底层原理出发真正理解为什么这些工具有快慢和“搜得到/搜不到”的差别。1. grep 的家族生态为什么要重新审视一个 1972 年的老命令1.1 从 ed 到 grep命令为什么叫 grep先说个冷知识。grep 这个名字来源于ed编辑器里的一个全局正则打印命令g/re/p也就是 global / regular expression / print 的缩写。它的使命从一开始就很明确在输入文本里按正则表达式查找把匹配的行打印出来。这套逻辑非常简单所以由它衍生出来的一整条工具链都保持着惊人的一致性输入一行文本判断是否匹配输出匹配的行返回退出码。退出码 0 表示找到匹配1 表示没找到2 表示遇到错误。这个约定到今天依然是所有 grep 系工具共同遵守的基础规则。问题在于grep 诞生于磁带和慢速终端的年代它当时的很多默认行为比如不递归搜索、不区分隐藏文件、不做多线程优化在现在的开发场景里已经变成了需要反复回避的痛点。于是才会有后来百花齐放的 grep 替代品ack、agthe_silver_searcher、rg以及各种专注特定场景的专用工具。1.2 grep 的三大不顺手性能、规则、默认行为把 grep 在日常使用中的不舒服总结起来基本是三件事。第一是性能。GNU grep 本身其实已经相当快单文件搜索时它的类 BM 算法和大量 SIMD 优化非常优秀。但普通用户常用的grep -r是单线程递归扫描每打开一个文件都要做一次完整的状态初始化在文件数特别多、文件体积特别大的场景下性能远远不如能并行扫描的现代工具。第二是规则。普通 grep 默认不会自动读取.gitignore不会自动跳过.git目录也不会自动识别二进制文件与文本文件。于是你在项目里搜某个函数名出来的结果里夹着一堆历史快照、编译产物、node_modules 甚至图片文件需要额外拼一堆--exclude-dir参数才能勉强过滤掉。第三是默认行为不够稳。不同 Linux 发行版自带的 grep 版本可能不同有些默认支持 PCRE用-P开启有些没有编译进来在多字节字符的处理上locale 设置不同也会影响匹配结果。这些“环境差异”在脚本里特别容易埋雷。1.3 大盘点思路rg、tgrep、rawgrep、zg 分别补了什么既然 grep 不是万能的那这一批新工具各自补的都是哪块短板rg补的是“大规模代码/文件检索”的短板用并行、内存映射、规则集把搜索速度和准确性拉到极致。tgrep补的是“结构化文本层级检索”的短板它对应的不是一行行的字符串而是一个个有层级结构的节点你可以直接按“父节点是 A、子节点是 B”这种条件去搜索。rawgrep补的是“默认行为堆叠太多”的短板它在概念上强调关闭各种附加逻辑让输入和输出尽量原样适合在调试正则或对比版本差异时有强迫症的人。zg实际使用中通常写作zgrep补的则是“压缩文件不解压就能搜”的短板它把解压和 grep 组合成一条流水线让你直接在大批量压缩日志里做检索。后面几节我一个一个展开讲。2. 代码搜索首选rgripgrep为什么能成为新默认2.1 ripgrep 的性能怎么来的先给结论rg是目前我在日常项目里使用频率最高的检索工具没有之一。它是用 Rust 写的但语言本身只是加分项真正让它快的原因主要有三个。第一个原因是并行遍历。rg默认会按 CPU 核数并行扫描文件文件内容读取和正则匹配分布到多个工作线程里在大目录下效率提升是数量级的。第二个原因是智能跳转。它默认遵守.gitignore、跳过隐藏目录和隐藏文件、跳过二进制文件绝不浪费一滴性能在无关数据上。第三个原因是内存映射优化。对超大文件它会用操作系统的内存映射机制做高效读取而不是把整个文件塞进用户态内存里慢慢啃。这三板斧合在一起效果非常明显。我在一个 50 万行代码、混合了图片和日志的仓库里测试过grep -r orderId .要跑 6 秒多rg orderId .基本上是 0.3 秒内出结果而且输出的结果还更干净因为不会被编译产物和二进制文件干扰。2.2 日常命令速查表从简单匹配到组合过滤rg 的入门成本极低因为 90% 的用法和 grep 是重叠的。下面这几个命令是我每天都在用的可以直接抄# 常规递归搜索输出行号和文件名 rg OrderService . # 只看文件名不显示具体匹配行 rg -l error src/ # 忽略大小写 rg -i orderid . # 全字匹配避免把 orderIdxxx 也搜出来 rg -w order . # 限定文件类型比如只看 Java 文件 rg -t java ListOrder . # 显示匹配行的上下文 rg -C 3 exception logs/backend.log # 反向匹配显示不含关键字的行 rg -v ^# nginx.conf # 计数统计 rg -c error logs/和 grep 不太一样的地方在于rg默认就是递归搜索当前目录不需要手动加-r输出有颜色高亮终端下阅读体验好很多文件名和行号也默认带上。2.3 常用参数与正则注意事项为什么 -P 和 -uuu 要谨慎用如果你只把 rg 当成“跑得快的 grep”那就太浪费了。它有一批参数专门用来处理 edge case。第一个是-uuu。-u在 rg 里不是“unique”的意思而是“反忽略”的等级一个-u不读.gitignore两个-u再搜索隐藏文件三个-u连二进制文件也一并扫描。我见过不少人在网上提问“rg 怎么搜不了隐藏文件”其实答案不是-a而是-uu。要注意这个参数的使用场景一般只在临时排查时用日常搜索加上它只会让结果变脏。第二个是-P也就是--pcre2。rg 默认用 Rust 自带的 regex 引擎不支持反向断言这类语法。如果你有一个习惯了 PCRE 的正则表达式比如(?key)\d直接丢给 rg 会报错。加上-P之后 rg 会切换到 PCRE2 引擎功能更全但性能也有一定下降。我的经验是日常简单匹配用默认引擎临时做复杂断言才开 PCRE2。第三个是--type-add。有时候项目里某些文件扩展名不在内置类型表里比如运维用的.tpl模板想让 rg 当成 html 处理可以这样rg --type-add web:*.tpl -t web csrf_token .这个配置也可以写进.ignore或全局配置文件里不需要每次敲。还有一个实际中很容易踩的坑rg 的-S--smart-case默认是开的。意思是如果模式里有大写字母就区分大小写如果模式全是小写就不区分大小写。这个行为很智能但也会让在脚本里的结果变得“不稳定”。如果你就是在写一个严谨的 CI 检测脚本建议显式用-s强制区分大小写别依赖 smart-case。2.4 什么时候还要回头用 grep说了 rg 这么多好话也得给老将留个位置。你至少应该把 grep 的这几个不可替代的场景记下来第一环境不可控的服务器上。很多老系统没有预装 rg你也不能为了搜一个文件就去装工具包这时候系统自带的 grep 是唯一选择。第二脚本可移植性要求高的情况。POSIX 规范里只保证 grep 存在rg 不保证。写给别人的脚本或者部署到受限容器里的脚本用 grep 更安全。第三单文件小规模搜索。文件很小的时候grep 和 rg 的差距可以忽略不计而 grep 的生态兼容性更好。另外也提一嘴ag和ack。agThe Silver Searcher是 rg 之前很流行的选择它的思路和 rg 几乎一样但项目已经基本停止迭代新项目直接选 rg 就好不用再走回头路。ack是 Perl 写的老牌工具功能不差但在大目录下性能比 rg 差两三个量级偶尔兼顾 Perl 正则习惯的人还在用整体上是明日黄花。3. tgrep面向结构化文本的层级检索3.1 tgrep 是什么和 grep 有什么不同很多人第一次听到tgrep会下意识认为它只是“快一点的 grep”实际上这个名字在开源领域里更多指代 Tree grep也就是“树结构的 grep”。它最早是语言学语料库领域用来搜索句子语法树的工具后来慢慢衍生出一些面向 XML、JSON 和其他结构化文本的检索实现。它和普通 grep 的本质区别在于普通 grep 针对的是“行”一行是一个不可拆分的字符串tgrep 针对的是“节点”输入文本被解析成树每一个节点都有类型、文本、父子关系和兄弟关系你可以按结构来写搜索条件。举个通俗的例子。普通 grep 想找“某本小说里所有对话”只能写grep “把每一行里带左引号的行全列出来。但如果文本已经被标成了段落和引号节点tgrep 就能直接问“哪些引号节点位于段落节点之下”过滤条件比行级匹配精确得多。在实际工程里我见过三类特别适合 tgrep 思路的用法一是 NLP 领域检索句法树。二是大规模配置文件里按父子层级定位设备配置。三是 JSON 日志里按对象路径去匹配内容。3.2 一套更通用的层级检索思路不靠工具名靠场景因为“tgrep”这个名字有专门的学术含义日常后端项目里直接把它拿来当命令用的情况反而不多。我更建议你把它理解成一组能力而不是死记某个二进制名字。在大多数生产环境里你其实可以用现成的工具组合出“类 tgrep”的效果。比如搜索 JSON 日志里的特定字段层级最顺手的不是 grep而是先拍平再过滤cat app.log | jq -r .. | objects | .level? // empty | grep -E ERROR|WARN这里jq负责把 JSON 的嵌套层级转成一行行的纯文本再用 grep 做行级匹配。相当于把一个树形结构先做了投影再把投影结果交给传统 grep。再比如处理 XML 文件直接对行做 grep 很容易被标签格式、命名空间干扰更可靠的做法是用xmllint --xpath先摘出要查的节点xmllint --xpath //server[port8080]/name/text() deploy.xml这个写法的检索依据是 XML 的节点结构而不是字符串表面正是 tgrep 类工具的强项。如果你经常处理 HTML/XML还可以考虑用 Python 的 lxml 写一段极短的解析脚本比硬拼正则稳得多。3.3 想要真正的 tgrep 命令去哪里找如果你确实就是想玩一下真正的 Tree grep 工具有两个方向可以查一个方向是 NLP 语料库常用的 tgrep它有一套专门的语法例如用表示“父节点为”表示“子节点为”搜索条件可以写成类似“找到所有动词短语且它的直接父节点是句子节点”这种形式。另一个方向是部分开源项目里自带的“结构化 grep”命令比如针对 AST 扫描的 semgrep某种意义上也可以看作“针对代码语法树的 tgrep”。我的建议是不要执着于找一个所有场景通用的 tgrep 二进制。把“结构化层级检索”这个思维装进脑子里平时用 jq、xmllint、semgrep 按场景组合就已经能解决绝大部分问题。这也符合工具发展的规律结构越复杂越不可能靠单一通用命令搞定一切。4. rawgrep关闭智能回到“原样匹配”的世界4.1 所谓 raw到底 raw 在哪这几年文本检索工具的趋势是越来越“聪明”自动忽略不受版本控制的文件、自动过滤二进制、自动压缩输出。大多数情况下这是好事但也有一批人踩过“智能”带来的坑明明文件就在那里rg 就是不搜它因为里面的规则太多了你根本想不到是哪一个默认开关拦住了结果。这时候你需要的可能是一个接近 raw 行为的工具或配置。我这里提到的rawgrep在开源软件库里其实有两个来源一个来源是某些 Unix 工具爱好者在早期 grep 源码基础上做的最小化重写目的是让行为尽量贴近最初“g/re/p”的朴素逻辑另一个来源是把 grep 的“额外参数全关掉”这个操作固化成一种习惯。核心思想是一致的关闭一切附加行为让输入、匹配、输出三者之间不再有隐形的中间规则。这也是我特别想强调的一点rawgrep 的价值不在于“快”而在于“确定”。当你在调试一个诡异的正则问题或者需要对比两份文件在纯文本层面到底差在哪时一个不友好的“智能工具”反而会帮倒忙。raw 模式给你的是一种可复现、可解释的行为。4.2 用现成工具构造一个 raw 模式好消息是你不需要真的去编译一个 rawgrep光靠 ripgrep 自带的反智能参数就能模拟出足够“raw”的行为。我平时是这样用的# 真·raw 模式不读 ignore 规则、搜隐藏文件、二进制当文本、不做 smart case alias rawrgrg -S --no-ignore --hidden --no-require-git -a --no-heading拆解一下这里每个参数的用意。--no-ignore表示不读.gitignore、.ignore等规则文件--hidden表示搜索隐藏文件-a表示把二进制文件也当文本处理--no-require-git表示即使不在 Git 仓库里也照常搜索--no-heading去掉分组输出让每行输出只有文件路径加匹配内容方便继续喂给下一级命令做文本处理。如果你习惯用 GNU grep也有对应的朴素姿势。这里的关键是把默认的智能行为显式关掉# 不递归、不做文件类型过滤、二进制也直接扫、不读任何 rc 文件 grep -a --no-messages --exclude-dir/.git -n pattern .对了很多 grep 和 rg 都会自动跳过符号链接想彻底 raw 就要在搜索前用find -L做一次解引用或者给 rg 加--follow。关于 raw 模式有一个经典使用场景线上排查时你明明看到tail -f server.log里滚过了一行包含关键 UUID 的错误但用rg UUID logs/搜索所有日志却什么都搜不到。这时候不要怀疑眼睛先检查日志文件是不是放在了.gitignore认识的某个目录下。用 raw 模式扫一遍结果大概率就出来了。4.3 适合 rawgrep 的实战场景以及它的边界我总结下来rawgrep 类行为在三种场景里是真正发挥价值的。第一种是跨文件内容对比。比如你想确认线上某个部署包里的配置和源码仓库里的配置是不是一致直接对两批文件跑 raw 搜索把结果 dump 出来做 diff可以排除掉版本控制系统忽略文件造成的假差异。第二种是二进制文件里的可读字符串提取。在二进制包里找明文 IPv4 地址、URL、密钥前缀等普通 grep 会因为检测到二进制而打住raw 模式配合strings命令往往能直接命中。第三种是写自动化校验脚本。脚本里需要保证“搜索逻辑只受显式参数影响”不希望某一天团队有人往.ignore文件里加一行规则导致搜索行为变化。这时候 raw 方式的确定性就是可维护性。但边界也很明显raw 模式没有智能排除扫描整个大目录时会产生大量噪音而且输出结果不一定按照你预期的编码展示。它的定位是“最后手段”和“调试工具”日常开发主力仍然是 rg。5. zg / zgrep压缩文件里的检索专家5.1 zgrep 的原理和基本用法“zg”这个写法在日常场景里大家更多使用的是zgrep。它做的事情可以概括成不用手动解压直接在 .gz 压缩包里执行 grep 搜索。原理并不复杂。gzip 压缩的文件经过gzip -dc命令会输出原始内容到标准输出zgrep本质上就是把这一步和grep通过管道串起来的封装。它内部会逐个解压文件再把解压后的内容喂给真正的 grep然后把匹配结果打上文件名前缀输出。优势在于你不需要在磁盘上生成临时解压文件对一个几十 GB 的压缩日志包也能按需读取。基本用法和 grep 非常接近# 搜索单个 gz 压缩包 zgrep -n ERROR app.log.2025-01-01.gz # 搜索多个压缩包 zgrep -i exception app.log.2025-01-0*.gz # 加上上下文行数 zgrep -C 3 NullPointerException backend.log.2025-01-02.gz注意zgrep的-E表示扩展正则-F表示固定字符串这跟 grep 的参数完全一致你不需要重新学一套语法。5.2 多类压缩格式与对应命令压日志的工具五花八门好在不同压缩格式都有对应的 grep 变体压缩格式命令备注gzipzgrep最常见bzip2bzgrep压缩率更高解压慢一些xzxzgrep高压缩比解压更慢zipzipgrep专门处理 zip 压缩包zstdzstdgrep新日志系统常用速度和压缩比都不错这些命令的行为模型都一样把“解压到标准输出”和“grep 搜索”串起来返回值和普通 grep 保持一致。只是底层解压算法不同性能差异会体现得很明显。如果日志经过了多级压缩比如.tar.gz里套了多个小文件用zgrep也不是不行但更推荐的做法是先列出压缩包内容再按需选择目标文件tar tzf full-backup-2025-01.tar.gz | head -50 tar xOzf full-backup-2025-01.tar.gz path/to/access.log | grep -E 403|404tar xOzf可以把压缩包里的指定文件直接解压到标准输出配合 grep 或 head 用既省磁盘又不浪费全量解压时间。5.3 压缩日志检索的生产级姿势在实际日志聚合系统不完善的团队里压缩日志检索是每次排障必做的脏活。我总结了一套相对不踩坑的操作流程。第一先确认文件名模式。很多团队的日志轮转策略是app.log、app.log.2025-01-01.gz、app.log.2025-01-02.gz这样递增。检索时间范围的第一件事是先ls -lh看看哪些压缩包落在这个时间窗口内避免把后续的搜索命令拉到整个目录所有文件上。第二用管道聚合而不是多次手工搜索。比如查某一小时内所有 5xx 错误可以这样zgrep -h 2025-01-07T10: app.log.2025-01-07*.gz | grep -E HTTP/(1\.[01])\ [5-9][0-9][0-9]其中-h表示不显示文件名前缀因为这一轮你关心的是纯日志内容文件名等下一轮再区分。第三善用合并参数。需要多个关键词同时匹配时不一定要写多个管道直接让 zgrep 用扩展正则做一次匹配速度会快很多zgrep -E ERROR|OutOfMemory|Connection refused app.log.2025-01-07*.gz | head -200一次性把候选行拉出来再通过上下文参数做精细定位比一步步管道过滤更容易保留现场信息。第四面对超大 gz 时别急着裸奔 zgrep。先检查压缩包本身有没有损坏否则解压到一半会返回一堆莫名其妙的错误gzip -t app.log.2025-01-07.gz确认没问题后再搜索。这道检查对动辄几十 GB 的生产日志尤其重要。5.4 大坑与小抄zgrep 常见的翻车点使用 zgrep 最容易翻的第一个坑是“管道里二次搜索太慢”。假设你要在一个 10GB 的压缩日志里搜某个 traceId再用这个结果反查另一个关键词如果写法是zgrep traceId123 huge.gz | grep timeout那么第一个 zgrep 会把所有包含 traceId123 的行都解压并输出其中绝大多数你并不关心。更高效的做法是直接写一个更精确的正则让 zgrep 一次性过滤zgrep -E traceId123.*timeout|timeout.*traceId123 huge.gz第二个坑是退出码。zgrep在没有匹配时返回 1在有部分文件损坏时可能返回 2这本来是很好的脚本友好行为。但如果你在没加set -o pipefail的 shell 脚本里使用zgrep ... | head管道退出码会被 head 覆盖脚本的后续判断逻辑可能失效。记得在关键脚本里显示处理退出码。第三个坑是编码问题。压缩日志经常是从 Windows 服务器导出的 GBK 编码文件直接 zgrep 搜中文会什么都搜不到。正确处理是先把解压流做编码转换再匹配zgrep -h app.log.2025-01-07.gz | iconv -f GBK -t UTF-8 | grep 订单过期注意这里用了zgrep -h 来清空文件名前缀同时利用 zgrep 只负责解压输出把真正的匹配逻辑交给 iconv 后面的 grep。第四个坑是性能认知。gzip 的解压是 CPU 密集型操作用 zgrep 在多个大文件上搜索瓶颈往往不在 grep 而在解压。如果机器上有pigz并行 gzip可以这样提速# 用 pigz 并行解压再搜索 pigz -dc app.log.2025-01-07.gz | grep --colornever 1f3870be274f6c49b3e31a0c6728957f在四核以上的机器上这个组合在很多场景里比原生 zgrep 快不少。6. 高频问题与排查实录6.1 rg 搜不到内容但 grep 能搜到怎么办这是我在社区里被问到最多的问题没有之一。明明文件里肉眼就有那个字符串rg却一声不吭用grep反而能搜出来。遇到这种情况按下面的顺序排查基本百发百中第一步确认是否被 ignore 规则拦住了。在项目根目录跑rg --files --no-ignore | grep 目标文件如果带--no-ignore能看到文件不带就看不到说明是.gitignore或.ignore挡住了。第二步确认是否因为文件是隐藏文件或二进制文件。加-uuu再搜一次如果结果出现说明默认过滤生效了。第三步确认文件类型过滤是否把目标文件排除掉了。如果你用了-t java却去搜一个.sql文件那当然搜不到。用--type-add补充类型或者干脆去掉-t参数。第四步想想正则对不对。rg 默认的 Rust regex 语法和 PCRE 有差异如果你拿一个带后行断言的 PCRE 正则去跑rg 会直接报错而不是静默失败。但如果你用的是--pcre2个别转义写法也会结果不同。最简单的验证方法是用grep -P交叉跑一下。这个方法论不仅适用于 rg也适用于任何“智能”的搜索工具。搜不到东西时第一反应不是怀疑数据而是先问“它的默认行为过滤了什么”。6.2 grep 中文乱码与编码问题中文日志乱码是另一个高频痛点。直接grep 错误 file不出来或者输出的中文全是乱码大概率不是正则写错了而是编码不匹配。Linux 文本文件常见 UTF-8而很多老应用输出 GBK/GB18030。遇到这种情况先看文件编码file -i app.log如果显示charsetiso-8859-1或charsetunknown可以尝试用 iconv 转码后再搜。比如把 GBK 转成 UTF-8iconv -f GBK -t UTF-8 app.log | grep 订单过期如果文件实在太大用iconv全量转换太慢可以先按行采样确认编码后只转换需要的窗口范围比如用sed -n 100,200p切片再转码。还有一个隐藏问题locale 环境变量没设好grep的中文匹配时可能因为 locale 不匹配导致性能急剧下降。在容器里遇到 grep 匹配特别慢先看环境echo $LANG $LC_ALL如果是空值或 POSIX建议显式设置export LANGC.UTF-8再搜。6.3 正则引擎不同导致的差异正则表达能力是 grep 系工具最容易“同词不同义”的地方。GNU grep 的默认基础正则BRE和扩展正则ERE在细节上跟 PCRE 差很多。比如在 BRE 里是普通字符表示一次或多次要写成\在 ERE 和 rg 里就是量词。同一段正则放到不同工具里结果不同是非常正常的。给一个最小对比表语法GNU grep 默认 BREgrep -E / zgreprg 默认引擎rg -P / grep -P一次或多次\或操作|后行断言不支持不支持不支持支持非贪婪匹配不支持不支持不支持支持如果你在写跨工具复用的正则最稳妥的策略是统一用固定字符串-F或者严格按 ERE 语法写然后所有工具都走-E。避免在默认 BRE 和 PCRE 之间反复横跳。6.4 想提升速度先学会给文件“减负”搜得慢有时候不是工具不行是你让它搜了太多不该搜的文件。优化顺序我一般这样排第一步缩小范围。能指定目录就不搜全仓库能用文件名后缀过滤就不裸搜。第二步排除无用目录。在 rg 的全局配置里我永远会加一行# ~/.config/ripgrep/ripgrep.conf glob !.git !node_modules !dist !build !vendor这样任何一次搜索默认都不碰这些目录速度和结果噪音同时改善。第三步控制输出量。只要不需要看全部匹配行就优先用-l只列出文件名、-c只统计次数或-m 20最多显示 20 行。第四步合理利用并行。多核机器上给 zgrep 类工具换用 pigz 做底层解压给 rg 就不用管了它默认多线程。这一套组合下来绝大多数搜索都能达到“手指刚离开回车结果已经出来了”的状态。7. 选型速查与我的使用习惯7.1 四个工具的定位差异最后把这几个工具放到一起做个速查方便你按场景直接对号入座工具核心优势主要场景使用门槛grep最通用POSIX 保证脚本、小文件、服务器排除很低rg快、默认行为智能代码仓库、大规模文本搜索低tgrep结构化层级检索树形数据、JSON/XML/语法树偏高rawgrep原样匹配、无隐形规则调试、对比、二进制字符串中zg/zgrep不解压直达内容压缩日志、压缩包检索低四者绝不是互相替代的关系。rg 是我日常的主力当需要处理结构化数据时我会想起 tgrep 的思路并灵活切换到 jq 或 xmllint 这类工具当搜索行为变得不可捉摸时rawgrep 式的“裸奔配置”能帮我把问题拉回可控范围只要涉及压缩文件zgrep 就是绕不开的伙伴。7.2 我给自己配置的常用命令我把自己平时的一套配置贴在下面都是 shell 别名和函数你可以按习惯微调# 代码搜索主力 alias qrg -n --hidden -g !.git -g !node_modules # 只看文件名 alias qlrg -l --hidden -g !.git -g !node_modules # raw 模式查隐藏文件、二进制也搜 alias rrgrg -uuu -a --no-heading # 普通 grep 保持颜色 alias ggrep --colorauto -n # 搜压缩日志 alias zgzgrep -n --colorauto # 并行解压搜日志 pigz_log_search() { local pattern$1 shift for f in $; do pigz -dc $f 2/dev/null | grep --colornever $pattern | sed s|^|$f:| done }有了这些别名日常进入项目后基本就是q加关键词的肌肉记忆。而pigz_log_search这个函数解决了一个 zgrep 不能做并行解压的问题实测在压缩日志比较多的机器上能省不少时间。7.3 最后一个小建议把“搜索”当工程来对待说了这么多我最想传达的一句话是搜索不是一个“敲命令这么简单”的动作。一次好的搜索结果取决于你对文件结构的理解、对编码和忽略规则的敬畏、对正则引擎差异的敏感以及一点点把工具组合起来的想象力。我个人踩过无数次坑之后最受益的一个习惯是凡是超过 10 秒的搜索我都会花半分钟想想“是不是我让他搜的东西太多了”凡是搜不到的时候我都会先检查“是什么智能行为把它挡住了”。你要是也能养成这种习惯那不管是 grep、rg、tgrep 还是 zgrep在你手里都会变成趁手的兵器。