grep —— 从海量文本里捞出那一行

运维工具手册 04-文本处理 4.6 KB 原文

grep —— 从海量文本里捞出那一行

分类:04 文本处理 适用:全平台(GNU grep / BSD grep;ripgrep 更快) 一句话:日志检索、配置查错、代码搜索的基础工具,掌握上下文参数能省一半时间。

1. 它解决什么问题

  • 在几个 G 的日志里找一次报错,还要看前后几行。
  • 统计某个错误出现了多少次。
  • 递归搜索目录里哪份配置写了某个值。
  • 从命令输出里筛出需要的行。

2. 基础用法

grep 'error' app.log
grep -i 'error' app.log            # 忽略大小写
grep -v 'DEBUG' app.log            # 反向:不含
grep -c 'error' app.log            # 只输出匹配行数
grep -n 'error' app.log            # 带行号
grep -l 'timeout' *.log            # 只输出含匹配的文件名
grep -L 'pattern' *.log            # 输出不含匹配的文件名
grep -H 'error' *.log              # 强制显示文件名
grep -h 'error' *.log              # 不显示文件名

3. 上下文(排障最重要)

grep -A 5 'ERROR' app.log          # 后 5 行
grep -B 5 'ERROR' app.log          # 前 5 行
grep -C 5 'ERROR' app.log          # 前后各 5 行
grep -A 20 -B 2 'Exception' app.log

日志里的异常堆栈通常是"一行错误 + 几十行堆栈",-A 30 才看得全。

4. 递归与文件筛选

grep -r 'listen 80' /etc/nginx/            # 递归
grep -rn 'Listen 8080' /etc/              # 递归 + 行号
grep -r --include='*.conf' 'root' /etc/nginx/
grep -r --exclude='*.log' --exclude-dir='.git' 'TODO' .
grep -rI 'pattern' /data                  # -I 跳过二进制文件

--include / --exclude 可多次使用,比事后 | grep 高效。

5. 正则与扩展

grep 'error\|fail' app.log              # 基础正则:或
grep -E 'error|fail|timeout' app.log    # 扩展正则(推荐)
grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}'   # 日期开头
grep -P '\d{3,}' app.log                # Perl 正则(\d、\b、断言)
grep -o 'user_id=[0-9]*' app.log        # 只输出匹配部分
grep -w 'root' /etc/passwd              # 全词匹配
grep -x 'exact line' file               # 整行匹配
grep -F 'a.b.c' file                    # 固定字符串(不解释正则,快)
参数 正则类型 说明
默认 BRE | \+ \? 需转义
-E ERE `
-P PCRE 支持 \d、\w、前瞻断言
-F 无 按字面量匹配,最快

6. 常用组合

统计

grep -c 'ERROR' app.log                       # 次数
grep -o 'status=[0-9]*' app.log | sort | uniq -c | sort -nr   # 分布
grep 'ERROR' app.log | awk '{print $5}' | sort | uniq -c | sort -nr | head

多条件

grep -E 'ERROR|FATAL' app.log                         # 或
grep 'ERROR' app.log | grep 'order-service'           # 与
grep 'ERROR' app.log | grep -v 'healthcheck'          # 排除噪音

配合 awk/sed

grep 'POST /api' access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head
grep 'ERROR' app.log | sed 's/.*ERROR //' | head

二进制与压缩文件

grep -a 'pattern' binary.dat          # 当文本处理
zgrep 'ERROR' app.log.1.gz            # 直接搜 .gz
bzgrep / xzgrep                       # 其他压缩格式
grep -r --include='*.gz' -l 'x' .     # 不会自动解压,需 zgrep

7. 实战场景

7.1 定位一次报错的完整上下文

grep -n -A 40 -B 5 'NullPointerException' app.log | head -80

先拿到行号,再用 sed -n '12345,12390p' app.log 精确查看。

7.2 统计各接口 5xx 数量

grep -E '" 5[0-9]{2} ' access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head -20

7.3 在配置目录里找出所有监听端口

grep -rn -E '^\s*(listen|port)\s' /etc/nginx/ /etc/redis/ 2>/dev/null

7.4 排除日志噪音

grep 'ERROR' app.log \
  | grep -v -E 'HealthCheck|ReadinessProbe|/metrics|kube-probe'

7.5 搜索大文件时提速

# 1. 先确认文件大小,考虑只搜尾部
tail -n 500000 app.log | grep 'ERROR'

# 2. 用固定字符串
grep -F 'Connection refused' app.log

# 3. 用 ripgrep(快 5~10 倍)
rg 'Connection refused' /var/log/

8. 常见坑

  1. 没加引号导致模式被 shell 解释:grep *.log 会展开文件名,模式要用引号。
  2. grep -r 会跟软链接目录:加 -r(不跟随)或 -R(跟随),GNU grep 的 -r 默认不跟随符号链接。
  3. 二进制文件提示 Binary file matches:加 -a 或 -I。
  4. grep 匹配的是行:一行里有多个匹配也只算一行,计数要多用 -o。
  5. -c 与 -o 组合:grep -o ... | wc -l 才是匹配次数,-c 是行数。
  6. 中文/编码问题:日志是 GBK 时先 iconv -f gbk -t utf-8,否则匹配不到。
  7. -P 不是所有平台都有:BSD/macOS 的 grep 不支持,用 ggrep 或 rg。
  8. 大文件内存:GNU grep 会 mmap,通常没问题;管道输入时逐行处理,但 -A/-B 需要缓冲。
  9. --exclude-dir 对绝对路径起点无效:从当前目录开始搜更可靠。
  10. 颜色影响脚本解析:--color=always 会插入 ANSI 转义,管道里别开。
  11. grep -v -v 不是双重否定,多个 -v 只生效一次。
  12. ^ 与 $ 在 -z 模式下行为不同:按 \0 分段时要注意。

9. 速查表

grep 'pat' file                       # 基础
grep -i 'pat' file                    # 忽略大小写
grep -v 'pat' file                    # 反向
grep -c 'pat' file                    # 行数
grep -n 'pat' file                    # 行号
grep -C 5 'pat' file                  # 上下文
grep -A 20 'pat' file                 # 后 20 行
grep -E 'a|b' file                    # 扩展正则
grep -P '\d+' file                    # Perl 正则
grep -o 'k=[0-9]*' file               # 只输出匹配
grep -F 'a.b' file                    # 固定字符串
grep -r --include='*.conf' 'x' /etc   # 递归+筛选
grep -rl 'x' .                        # 只列文件
zgrep 'x' file.gz                     # 搜压缩包

# 统计
grep -o 'pat' f | sort | uniq -c | sort -nr
grep —— 从海量文本里捞出那一行 - 知识库 - 起航社区