grep命令详解:从参数到正则,实战排查Linux文本

发布时间:2026/9/7 15:08:43
grep命令详解:从参数到正则,实战排查Linux文本 这期写 grep我估计是这个系列里被催更最狠的一篇也是日常高频到几乎每个 Linux 从业者都绕不开的一个命令。grep 全称是 Global Regular Expression Print翻译成人话就是从一堆文本里按规则抓内容然后把命中的行或者命中部分打出来。查日志、查进程、查配置、查硬件信息、刷面试题到处都有它。这篇我把 grep 拆成三块来讲参数怎么用、正则怎么写、以及实战中那些容易翻车的组合姿势。新手可以从头到尾敲一遍老手直接跳到自己感兴趣的部分重点看看第 3 节和第 5 节里那些“你以为你懂了实际一跑就出问题”的细节。1. 先搞清楚 grep 到底能干什么1.1 一个命令撑起一半的查错场景我经常跟新同事说Linux 下排查问题80% 的时间都在查文本查文本的 80% 又在用 grep。你登录一台服务器后干的第一件事是什么多半是打开日志找异常关键字或者ps -ef | grep看进程在不在、跑没跑起来。这些动作背后都是同一个核心操作从一个文本流里筛出你想要的信息。grep 之所以重要倒不是因为它的实现有多复杂而是因为它遵守了 Unix 哲学里“一个程序干一件事干到极致”的原则。它不关心输入来自文件、来自标准输出还是来自其他命令的管道只要是文本它就能帮你过滤、提取、统计。这看起来很简单但配合上管道符这种组合方式它就变成了整个 Shell 生态里的“筛子”前一个命令输出的结果在它那儿过一遍剩下的就是你要的答案。另外grep 在所有发行版和几乎所有 Unix 系统里都是默认自带的。不管你是 Ubuntu、CentOS、Debian 还是国产化系统也不管是物理机、虚拟机还是容器里只要有 Shell基本就有 grep。这意味着它不存在“环境没有装”的问题也不用担心换一套环境后发现命令消失了。1.2 grep 的三种使用姿势grep 的基本形态我从实际使用中总结了三种场景你可以对着理解一下第一种是直接搜文件内容比如grep error /var/log/messages。它会把文件里所有包含 error 的行打印到终端这是最经典的用法。第二种是搜标准输入或管道传过来的内容比如ps -ef | grep java。这里 grep 接收的是前一个命令的输出而不是某个具体文件。这种方式在我们看进程、看网络连接、看磁盘信息时极其常见也是这篇文章后面大量实战示例的基础。第三种是递归搜目录比如grep -r TODO src/。它会把目录下所有文件都翻一遍把所有出现过 TODO 的文件和行都列出来。这个用法在项目代码里找遗留标记、找硬编码配置时非常省事。这三种姿势覆盖了绝大多数日常场景。记住一点grep 最擅长的是“从一堆东西里挑出你关心的那部分”它不擅长做复杂的文本替换和流式编辑那是 sed 和 awk 的活。搞清楚边界用起来就顺手了。2. 核心参数逐个拆解这一张表就够用2.1 必会参数速查与手把手示例grep 的参数不算多但每个都挺常用。我按从高频到低频的顺序整理了一份表格后面再挨个演示。参数作用典型场景-i忽略大小写匹配 nvidia、NVIDIA、Nvidia 等不同写法-v反向匹配取不包含模式的行过滤注释行、排除 grep 自身进程-n显示行号日志定位时知道具体在哪一行-c统计命中的行数统计某关键字在文件里出现多少行-l只列出包含匹配内容的文件名在目录里快速找出哪些文件命中了-L只列出不包含匹配内容的文件名找出没命中模式的文件-o只输出匹配到的部分不输出整行精确提取 IP、时间戳等片段-r / -R递归搜索目录在项目目录里全局查找关键字-w精确匹配整个单词搜 error 时避免匹配 errors、error404-x整行精确匹配精确匹配某行内容-E使用扩展正则使用 |、、? 等正则语法-A 数字同时显示匹配行的后 N 行看异常日志时关注堆栈下方内容-B 数字同时显示匹配行的前 N 行看异常发生前上下文-C 数字同时显示匹配行前后各 N 行一次看全上下文--include只搜索指定类型文件只搜.log、.conf、*.java--exclude排除指定类型文件跳过.tmp、.bak 等文件--exclude-dir排除某些目录跳过 node_modules、.git 等目录--colorauto自动高亮匹配内容终端里直观看到命中片段-q静默模式只要结果状态脚本里做判断用-I忽略二进制文件避免日志目录里出现 Binary file matches我一个个举例子。-i这个参数在查硬件信息和日志的时候救命。比如显卡型号lspci | grep -i nvidia就能同时匹配 NVIDIA、nvidia、Nvidia你不需要去猜设备厂商在输出里到底用了什么大小写。-v用得也特别多。查进程的时候ps -ef | grep java会把grep java本身也捞出来这时候后面接一个| grep -v grep就能把这一行噪音过滤掉。后面第 5 节我会详细说这个坑。-n适合排查日志。你搜索一个异常关键字光知道哪一行命中了还不够还得知道它在文件里的第几行这样后续你想用 sed 之类的工具进一步处理就很方便。-o是提取型参数是我个人最喜欢也最容易被人忽略的一个。它的意思是只输出匹配到的部分而不是整行。比如你想提取日志里所有的 IP 地址grep -oE ([0-9]{1,3}\.){3}[0-9]{1,3} app.log就能把所有 IP 一个个摘出来配合 wc -l 能统计出现次数配合 sort | uniq -c 能统计去重后的频次。2.2 参数组合拳从日志里定位一次线上故障单个参数好理解真正体现 grep 实力的是组合使用。我虚拟一个场景线上 Java 服务报 OOM日志文件几百兆你不可能打开编辑器慢慢翻。第一步先找到异常出现的行号grep -n OutOfMemoryError app.log。如果几百兆文件里面匹配的行很多可以再加 head 或 tail 控制范围或者先定位首次报错范围。第二步看异常上下文grep -A 30 -B 5 OutOfMemoryError app.log。-A 30 表示把报错后面 30 行堆栈打出来-B 5 表示把报错前 5 行也打出来这样你能看到报错触发前最后一次业务操作是什么以及堆栈里具体是哪个类哪一行崩了。第三步如果你想看整个文件里 OOM 出现了多少次可以用grep -c OutOfMemoryError app.log。这里要注意-c 统计的是“命中的行数”如果一行里出现多次 OOM它也只算一行。想精确到“出现了多少次”应该用grep -o OutOfMemoryError app.log | wc -l。第四步如果你怀疑某个业务接口引发的可以先把所有包含该接口路径的日志行抓出来再做反向过滤排除健康检查、心跳检测等噪音grep 支付回调 app.log | grep -v health | tail -50。这个组合过程其实就是 grep 在真实运维里的典型用法先扩大范围再逐步缩小最后锁定目标。3. 热词里那些 grep 组合命令到底怎么用3.1 ps -ef | grep java 查看启动时间这里有个大坑网上搜“ps -ef | grep java 查看启动时间”这个用法的人非常多因为大家确实想知道某个 Java 进程是什么时候启动的是不是被人重启过。但这里有一个特别容易误导新手的地方。ps -ef 输出里确实有一列叫 STIME表示进程启动时间但它的精度根本不是时分秒级别而是分段的如果进程是今天启动的显示 HH:MM如果是今年启动但超过了一天显示“月日”比如 Sep08如果是更早跨了年份的直接显示年份。所以你真想精确到秒级去看启动时间靠 ps -ef 是不行的。我自己的做法是分两种需求处理。如果只是想看格式化的启动时间用ps -eo pid,lstart,cmd | grep javalstart 会输出完整的启动时间精确到秒。如果你想看这个进程已经跑了多久用ps -eo pid,etime,cmd | grep javaetime 会输出类似 02-03:15:40 这种格式表示已运行 2 天 3 小时 15 分 40 秒。这是面试里一个高频率考点ps -ef 里的 STIME 和 ps -lstart 到底有什么区别。我建议每个用 Linux 干活的人都把这两个命令的实际输出跑一遍记住差异下次就不会在紧要关头拿一个只有时分秒或只有日期的时间去跟别人对线了。3.2 lspci | grep -i nvidia、ps aux | grep -i openclaw 这类查询套路lspci | grep -i nvidia这个组合本质上是在查硬件设备信息。lspci 把机器上所有 PCI 总线上的设备列出来网卡、声卡、显卡、USB 控制器都在里面输出内容非常长直接用眼睛找很费劲。这时候 grep 就派上用场了-i nvidia是为了容错毕竟不同机器上显卡厂商在 lspci 输出里的大小写格式不完全一样。同样的套路还能用在很多地方。查 CPU 型号用lscpu | grep -i model name查内存用free -h查磁盘挂载用lsblk | grep -i disk查网卡 IP 用ip addr | grep -i inet 。大家不妨把这个思路记下来凡是输出结果长得比较长的命令都可以加个管道接 grep 做过滤这是 Linux 运维里的高频肌肉记忆。ps aux | grep -i openclaw这个组合跟查硬件一样属于“进程查询套路”。ps aux 会列出系统所有进程以及它们的用户、CPU、内存占用、终端、启动时间、命令行等字段。这里要说明一下 ps aux 和 ps -ef 的区别aux 是传统 BSD 风格输出里带 %CPU、%MEM 这一列适合看资源占用-ef 是 SysV 风格带 PPID 父进程 ID适合梳理进程父子关系。两者日常都会用到按下需求选。grep -i openclaw的意思是不区分大小写地查找所有名字里带 openclaw 的进程。我自己查服务存活时最常用的就是类似写法ps aux | grep -i java | grep -v grep。如果有人跟你说某个服务进程不见了你先跑这几步基本就能判断出进程到底在不在、还在不在正常占资源。3.3 ps -e | grep apt 然后 kill这个操作请千万停手热词里有一条“通过 ps -e | grep apt 列出所有带有 apt 字样的进程然后用 kill 命令将其一一杀死”我猜提这个问题的人大概率是遇到了 apt 卡死或 dpkg 锁的问题想用暴力方式解开。这里我必须非常认真地提醒一句这个操作千万要谨慎盲盒式地 kill 掉所有含 apt 的进程很容易把系统搞出更大的麻烦。先说命令本身的原理。ps -e 等价于 ps -A列出系统上所有进程不含辅助信息。接了 grep apt 之后所有名字或者命令行里包含 apt 字符串的进程都会被捞出来。这包括你正在跑的 apt-get、apt还包括后台的 aptd、apt-check以及一些系统组件里命令行参数恰好带 apt 的进程。更尴尬的是grep 本身也会匹配到自己。如果你把这些 PID 全部 kill 掉最直接的后果是打断正在进行的 apt 事务。apt 在安装软件时是有事务状态的如果中途被杀很可能留下 dpkg 中断的状态下次再执行安装命令就会提示 dpkg was interrupted必须手动修复才能继续。如果是 aptd 之类的后台服务被杀了系统自动更新机制也会被连带影响安全补丁错过的后果在某些场景下比你现在想解的锁还严重。那遇到 apt 卡死到底该怎么办我的经验是分两步。第一步先查看是谁锁住了 dpkg 相关文件lsof /var/lib/dpkg/lock-frontend或者ps -ef | grep -E apt|dpkg看清楚是哪个进程在读、在等。第二步如果确认是一个十几分钟没动过的 apt-get 进程就针对它的 PID 单独 kill或者用kill -9 PID收尾。真想按名字杀可以用pkill -x apt-get或pkill -x apt-x 表示精确匹配整个进程名不会误杀 aptd。一句话总结grep 负责找出候选进程但“杀不杀”和“怎么杀”是你自己做的决策别让一个筛子替你做全部判断。4. 正则表达式grep 的灵魂学会这几招就够用4.1 基础正则与扩展正则到底差在哪grep 之所以强大很大程度上归功于它自带正则表达式引擎。很多人以为 grep 就是关键字匹配其实它的模式可以是正则能匹配一类文本而不是一个字面字符串。grep 默认用的是基础正则语法也叫 BRE。BRE 里的元字符只有^、$、.、*、[]这几个。比如^error表示行首以 error 开头error$表示行尾是 errorerr.表示 err 后面跟任意一个字符。问题在于BRE 对|、、?、()、{}这些更高级的语法默认不认可需要转义才能用。比如你要在同一行里匹配 error 或 warningBRE 下得写成grep error\|warning。这写起来太别扭了所以我强烈建议你直接加-E参数使用扩展正则 ERE。扩展正则是正则本身更自然的一套语法加号、问号、竖线、括号、花括号都能直接用不需要转义。我个人习惯是所有稍微复杂一点的匹配都上-E把基础正则留给简单场景。别小看这一点它能让你的命令读起来爽很多grep -E error|warning app.log比grep error\|warning app.log可读性高一个量级。4.2 实战正则抓 IP、抓时间戳、过滤空行我选几个日常出镜率非常高的正则写法大家可以直接拿去用。抓 IP 地址grep -oE ([0-9]{1,3}\.){3}[0-9]{1,3} app.log。这里用到了扩展正则里的{m,n}重复计数[0-9]{1,3}表示匹配 1 到 3 位数字后面跟一个点重复 3 次最后再跟一段 1 到 3 位数字。实际场景里如果你只要公网 IP可以对结果再过滤一下或者后续用 awk 判断网段。抓时间戳日志里最常见的格式是2025-01-06 14:30:25正则可以写成grep -oE [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} app.log。这个写法很直观年月日时分秒各占几个数字中间用连字符和冒号分隔一眼就能看出来。过滤空行和注释行看配置文件时我经常用grep -Ev ^\s*$|^\s*# /etc/nginx/nginx.conf。-E 支持扩展正则^\s*$匹配全是空白的行^\s*#匹配以 # 开头的注释行前面可能还有空格-v 表示反向过滤。跑完之后配置文件去掉注释空行只剩有效配置阅读体验直线上升。还有一个细节很实用-o配上正则后提取能力非常强。比如你想统计某日志里 5 开头的状态码有哪些grep -oE status:5[0-9]{2} access.log | sort | uniq -c | sort -rn几秒钟就能得到一张频率表排查故障时效率极高。5. 常见问题与排查技巧实录5.1 为什么 grep 结果里老是出现命令自己这是每个 Linux 新手都会疑惑的问题。你执行ps -ef | grep java结果里总会多出一行类似grep --colorauto java的进程。原因是 grep 命令在匹配时它自己的命令行参数里包含 java 这个字符串ps 输出的内容里自然就带上了于是它也被自己筛出来了。解决办法有几种。最朴素的是在后面加一个| grep -v grep把名字里含 grep 的那一行丢掉。我见过很多老手这么干虽然多打了一段但直观可靠。另一个更优雅的写法是ps -ef | grep [j]ava利用正则字符集匹配[j]去匹配字母 j而 grep 进程自己的命令行里是grep [j]ava中间的[j]并不会被[j]这个模式匹配到所以自然就不会列出自己。这个技巧在面试里偶尔会被问到原理其实很简单记得住就行。如果只是查进程是否存在我更推荐直接上pgrep -f java。pgrep 本身不会匹配自己-f 参数支持匹配完整命令行用起来干净利落。查完还能配合pgrep -f java | xargs ps -fp展示详细内容。5.2 grep 搜索卡住不动多半是没限制范围新手最喜欢干的一件事就是grep -r 关键字 /想全盘搜一遍结果命令跑半天跑不完。这其实是 grep 最常见的翻车姿势。系统根目录下有大量的二进制文件、伪文件系统目录/proc、/sys、软链接循环grep 引擎会在里面浪费海量时间。正规做法是给搜索范围设置边界。一种是用--include只搜指定类型文件grep -rn 关键字 /data/apps --include*.log另一种是用--exclude-dir排除大目录grep -rn 关键字 / --exclude-dir{proc,sys,dev,run,tmp}。我自己的习惯是能明确范围就先明确范围别动不动就全盘搜如果确实要搜全盘必须加上-I忽略二进制文件并且排除关键虚拟目录。如果你发现 grep 卡住了可以先按 CtrlC 打断确认一下是不是查了 /proc 或 /sys 这种动态目录。这两个目录内容不是真实磁盘文件而是内核接口的实时映射数据量虽然不大但语义特殊没必要用它做关键字搜索。5.3 统计关键字次数时一个经典的语义陷阱我面试人的时候经常问一个问题怎么统计一个日志文件里 error 这个单词出现了多少次有相当一部分人回答grep -c error app.log。这个答案对了一半但存在一个陷阱。grep -c统计的是“匹配到的行数”不是“匹配到的次数”。如果某一行里同时出现了 5 个 errorgrep -c只会计 1。所以在真正要算“出现多少次”的场景里正确姿势是grep -o error app.log | wc -l。-o 把每次匹配到的 error 单独摘出来输出一行wc -l 统计总行数这才是真正的出现次数。这两个口径在日志量小的时候差距不大但在分析大日志时差异会很显著。大家根据自己需求选想知道多少行出了错用 grep -c想知道总的报错频次用 grep -o | wc -l。6. 一些关于 grep 的个人习惯和补充技巧最后分享几个我用 grep 时积累的小习惯没有系统性但每个都是实战中磨出来的。第一个是终端高亮。现在很多发行版默认给 grep 配了--colorauto如果你用的系统没有建议在 shell 配置文件里加一个别名alias grepgrep --colorauto。别看只是加上颜色在日志里找关键字时视觉引导作用非常大能省不少眼睛。注意别直接用环境变量 GREP_OPTIONS那个已经不建议使用了用 alias 是最稳妥的方案。第二个是结合 tail -f 做实时跟踪。调试日志时我经常开两个终端一个跑服务一个执行tail -f app.log | grep --line-buffered ERROR。关键点在于--line-buffered参数它告诉 grep 每读到一行就立刻输出而不是攒一批再输出。缺了这个参数你可能会等很久看不到实时日志还以为是系统卡了。这是非常实用的一个小细节。第三个是用 grep 从一个命令结果里快速提取关键信息后再接 awk、sort、uniq 做二次加工。比如统计访问日志里哪个 IP 请求最多grep -Eo ([0-9]{1,3}\.){3}[0-9]{1,3} access.log | sort | uniq -c | sort -rn | head -10。这一条链路上grep 负责提取剩下的管道命令负责汇总排序。学会这种“流水线思维”很多繁琐的统计任务几句话就能解决。第四个建议是如果你在做大规模代码搜索或日志搜索时感觉 grep 越来越力不从心可以去了解一下 ripgrep 这个现代替代品。它搜大目录的速度比 grep 快很多还天然支持 Git 忽略规则。但说到底grep 依然是所有 Linux 系统默认自带、任何环境都能用的底线工具你花时间学它的每一分功夫都不会浪费。写到这里突然想到一个场景有次凌晨两点我被叫起来查问题日志文件里全是各种无意义的基础日志一片白噪声。我用了grep -v health app.log | grep ERROR -B 1 -A 10 | tail -80十几分钟锁定了根因。那种在混乱里用一把趁手的筛子精准捞到目标的感觉就是我一直建议大家把基础命令训练到肌肉记忆的原因。grep 不难难的是你对它的每一个参数和正则都有足够清晰的边界感。把这篇文章里的命令挨个敲一遍你会发现接下来的巡检、排障、写脚本都会顺畅非常多。