正则表达式:从日志里捞出你想要的那一行

正则不用学全,掌握十来个模式就能解决日常 90% 的文本处理。这篇只写我用得上的。

一、先选对工具

工具正则方言特点
grepBRE(基础)默认,+?() 要转义
grep -EERE(扩展)推荐+?|() 直接用
grep -PPCRE支持 \d \b 等,功能最强
sedBRE / sed -E替换为主
awkERE按字段处理更方便
bash
# macOS 自带 grep 对 -P 支持不全,可用 ggrep(brew install grep)
grep -E "error|warn" app.log

二、最常用的几个模式

模式含义例子
.任意单字符a.c 匹配 abc
*前一个 0 次或多次ab*
+前一个 1 次或多次[0-9]+
?前一个 0 或 1 次https?
^ $行首 / 行尾^GET
[...]字符集[0-9a-f]
()分组,可反向引用(ab)+
|error|fatal
{m,n}重复次数[0-9]{2,4}

匹配 IP 地址

bash
# 简单够用版(不校验 0-255 范围,日志场景足够)
grep -oE "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" access.log

# 严格版(了解即可,太长不好念)
grep -oE "\b(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\b"

匹配时间戳

bash
grep -E "^2026-[0-9]{2}-[0-9]{2} [0-9]{2}:" app.log

# Nginx 日志的 [19/Feb/2026:10:00:01
grep -oE "\[[0-9]{2}/[A-Za-z]{3}/[0-9]{4}:[0-9:]{8}" access.log

三、grep 的实用参数

bash
grep -E "pattern" file
grep -i             # 忽略大小写
grep -v             # 反向:不匹配的行
grep -n             # 显示行号
grep -c             # 只数有多少行
grep -o             # 只输出匹配的部分(配合统计特别有用)
grep -A 3 -B 2      # 显示匹配行前后几行(看堆栈必备)
grep -r             # 递归目录
grep -l             # 只列出文件名

# 组合例子:统计日志里 IP 出现次数
grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}" access.log | sort | uniq -c | sort -rn | head

四、sed 替换与反向引用

bash
# 基本替换
sed -i "s/old/new/g" file

# 用 \1 \2 引用分组
echo "2026-09-17" | sed -E "s/([0-9]{4})-([0-9]{2})-([0-9]{2})/\2\/\3\/\1/"
# 09/17/2026

# 只在匹配的行做替换
sed -i "/^server_name/ s/example.com/www.bzii.cn/" nginx.conf

# 删除空行 / 注释行
sed -i "/^$/d; /^#/d" conf

# 注意:-i 直接改文件,先备份或用 -i.bak
sed -i.bak "s/old/new/" file

五、awk 里用正则

bash
# 某列匹配
awk '$9 ~ /^5[0-9]{2}$/ {print $7, $9}' access.log

# 不匹配
awk '$1 !~ /^10\./ {print}' access.log

# 按正则切分字段(默认按空白)
awk -F"[,:]"{print $1, $3}'

# 用正则做范围处理
awk '/BEGIN/,/END/ {print}' file

六、实战:几个我常用的组合

bash
# 1. 找出访问量异常高的 IP(>1000 次)
awk '{print $1}' access.log | sort | uniq -c | awk '$1 > 1000'

# 2. 找出慢请求(最后字段 request_time > 1s)
awk '$NF > 1 {print $7, $NF}' access.log

# 3. 从配置文件里抠出所有域名
grep -oE "server_name[^;]*;" /etc/nginx/sites-enabled/* | sort -u

# 4. 批量改名:把 .jpeg 改成 .jpg
for f in *.jpeg; do mv "$f" "${f%.jpeg}.jpg"; done
# 或者用 rename(正则版)
rename "s/\.jpeg$/.jpg/" *.jpeg

# 5. 去掉 ANSI 颜色转义符(日志里带  时)
sed -r "s/\x1B\[[0-9;]*[mGK]//g" log

# 6. 提取 markdown 里所有链接
grep -oE "\]\([^)]+\)" README.md

七、容易踩的坑

  1. 贪婪匹配.* 会尽可能多吃,用 .*?(PCRE)或 [^"]* 限定;
bash
grep -oE "src=\"[^\"]*\"" page.html    # 正确:非引号字符
grep -oE "src=\".*\"" page.html           # 错误:会吃到行尾最后一个引号
  1. 点号要转义:匹配字面量的 .\.
  2. shell 引号:正则里有 $*\ 时用单引号包裹,否则 shell 会先展开;
  3. 中文匹配[\u4e00-\u9fa5] 在 PCRE 里可用,grep 默认不支持,改用 [^\x00-\x7F] 或直接匹配具体字符。
bash
# 找含中文的行
grep -P "[\x{4e00}-\x{9fa5}]" file

八、验证正则

bash
# 先小范围试,别直接 -i
grep -E "pattern" file | head -5

# sed 先不加 -i 看输出
sed -E "s/old/new/g" file | head

# 在线验证:regex101.com(选对方言)

我的习惯:先 grep 确认匹配范围对,再动 sed -i。 直接用 -i 改生产配置是事故高发动作。


正则不用背,存一份自己的「常用模式清单」,用的时候抄。