Linux 文本处理三剑客:grep、awk、sed 全面学习笔记

一站式掌握文本处理三大件:从入门到生产,再到性能与坑位。


目录

  1. 快速对比总览(何时用谁)

  2. 正则与编码小抄(BRE/ERE/PCRE、大小写与多行)

  3. grep —— 搜索与过滤

    • 用法速览与退出码
    • 参数总表(GNU 常见 + 兼容性提示)
    • 高频场景与示例
  4. awk —— 结构化提取、计算与格式化

    • 语言模型(pattern { action })
    • 命令行参数、内置变量、内置函数
    • 常见场景与脚本范式
  5. sed —— 流编辑(替换、删除、插入、分支、多行)

    • 命令行参数与脚本语法
    • 地址选择与编辑命令全集
    • 高频场景与示例
  6. 组合技:grep | awk | sed 管道实践

  7. 兼容性与性能优化(GNU vs. BSD/macOS、LC_ALL、递归、并行)

  8. 练习题与参考答案思路


1) 快速对比总览

工具 核心定位 强项 适合用来 不太适合
grep 搜索/过滤 高速正则匹配、递归过滤、上下文显示 在海量文本中找出匹配行、统计匹配数量、标色显示 跨行结构化处理、复杂格式化输出
awk 行→字段 的结构化处理 字段切分、聚合计算、条件筛选、格式化输出 CSV/TSV/日志字段统计、报表、轻脚本 大规模跨行替换(交给 sed)、复杂正则构建(可配合 grep
sed 流式编辑 原地(-i)替换、批量规则、地址选择、跨行编辑 批量改配置、清洗文本、多条替换规则 复杂统计/聚合(交给 awk

选择口诀:找谁?grep;算谁?awk;改谁?sed


2) 正则与编码小抄

  • BRE(Basic):传统基础正则(默认 grep -Gsed),如 \{m,n\} 需要反斜杠。
  • ERE(Extended):扩展正则(grep -Esed -E/-rawk 默认),+ ? | () {} 无需反斜杠。
  • PCRE:Perl 正则(grep -P,有些平台未编译开启),支持前后查找等高级特性。
  • 大小写-i 忽略大小写。更快匹配可设 LC_ALL=C(ASCII 语义、提升速度)。
  • 多行grep -z 结合 -P(?s) 可跨“行”(以 NUL 为分隔);sedN/H/Gawk 用自定义 RS

兼容提示:macOS 的 sed 是 BSD 版,-r,用 -Egrep -P 可能不可用。


3) grep —— 搜索与过滤

3.1 用法与退出码

grep [OPTIONS] PATTERN [FILE...]
# 或:
grep [OPTIONS] -e PATTERN ...
  • 退出码0=找到匹配;1=未匹配;2=错误(如文件不存在、正则错误)。

3.2 参数总表(GNU grep 常见)

✅ 为最常用;🧩 为兼容性或小众;📁 为文件系统相关;🎨 为显示控制

选项 含义 备注
-e PATTERN 指定匹配表达式,可多次 避免与以 - 开头的模式冲突
-f FILE 从文件读取模式(每行一个) 适合维护黑/白名单
-i, --ignore-case 忽略大小写
-v, --invert-match 取反(输出不匹配的行) 负过滤
-w, --word-regexp 整词匹配 以非字母数字边界界定
-x, --line-regexp 整行匹配 模式需匹配整行
-o, --only-matching 只打印匹配片段 提取值利器
-m NUM, --max-count=NUM 每文件匹配到 NUM 行后停止 搭配 -l 很快
-q, --quiet 静默(只看退出码) 管道/条件判断非常有用
-s, --no-messages 静默错误信息 与脚本搭配
-n, --line-number 显示行号
-H, --with-filename 始终显示文件名 默认多文件自动显示
-h, --no-filename 隐藏文件名 合并输出时干净
-c, --count 每文件只输出匹配计数 常与 sort -nr 组合
-l, --files-with-matches 仅输出匹配的文件名 反之 -L
-L, --files-without-match 仅输出匹配的文件名
-A N, --after-context=N 显示匹配后 N 行
-B N, --before-context=N 显示匹配前 N 行
-C N, --context=N 显示前后 N 行
🎨 --color[=WHEN] 高亮匹配 auto/always/never
🧩 -E, --extended-regexp 使用 ERE 等同历史 egrep
🧩 -F, --fixed-strings 字面量匹配(不当正则) 等同历史 fgrep最快
🧩 -G, --basic-regexp 使用 BRE(默认)
🧩 -P, --perl-regexp PCRE(前后查找等) 某些系统不可用
📁 -r, --recursive 递归读取目录 不跟随符号链接
📁 -R, --dereference-recursive 递归并跟随符号链接
📁 -d ACTION 目录处理:read/recurse/skip -r 控制精细行为
📁 --exclude=GLOB 排除匹配文件 递归时常用
📁 --include=GLOB 仅包含匹配文件 递归时常用
📁 --exclude-dir=GLOB 排除目录 递归时常用
🧩 --binary-files=TYPE binary/text/without-match -a=text-I=without-match
🧩 -a, --text 二进制按文本处理
🧩 -I 忽略二进制文件 不报“Binary file matches”
🧩 --line-buffered 行缓冲输出 低延迟管道
🧩 -b, --byte-offset 显示字节偏移 索引定位
🧩 -Z, --null 文件名后跟 NUL 分隔 xargs -0 使用
🧩 -z, --null-data 以 NUL 分隔输入“行” 跨行匹配技巧

BSD/macOS grep 不完全等同:--exclude-dir 等选项可能缺失,可改用 find ... -exec grep ... 组合。

3.3 高频场景与示例

(1) 递归查找关键字并显示上下文

grep -RIn --color -C2 "TODO|FIXME" src/

(2) 仅统计各文件中 ERROR 次数

grep -Rhc "^ERROR" logs/ | paste -d: <(find logs -type f | sort) - | sort -t: -k2,2nr | head

(3) 提取匹配片段(只要值)

# 从日志中提取 IPv4
grep -RoE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" /var/log | cut -d: -f2 | sort -u

(4) PCRE 跨“行”抓块(如 BEGIN..END)

# -z 将换行视作普通字符;(?s) 让 . 匹配换行
grep -Pzo '(?s)BEGIN.*?END' big.txt | tr '\0' '\n'

(5) 快速判断并按退出码处理

if grep -q "CRITICAL" app.log; then
  echo "发现致命错误,立即告警!"
fi

4) awk —— 结构化提取、计算与格式化

4.1 语言模型与基本用法

awk 'pattern { action }' file
# pattern 省略=>匹配所有行;action 省略=>打印当前行

示例:统计第 3 列之和(逗号分隔)

awk -F, '{sum+=$3} END{print sum}' data.csv

4.2 常用命令行参数

选项 含义 备注
-F FS 指定输入分隔符(字段分隔) -F, 处理 CSV
-v var=val 预先设置变量 -v OFS=","
-f prog.awk 从文件读取程序 便于复用与维护
-e 'program' 直接指定程序片段 可多次叠加
-- 结束选项处理 后续按参数传给程序

GNU awk(gawk)常见扩展选项(不同版本略有差异):

--posix(严格 POSIX 语义)/--traditional(传统兼容)/--lint[=fatal](提示可疑用法)/--sandbox(禁用 system()/文件 IO)/--debug(内置调试器)/--profile[=FILE](生成剖析信息)/--dump-variables[=FILE](导出全局变量与函数表)/--pretty-print[=FILE](美化输出程序)。

4.3 内置变量(高频)

变量 作用 示例
$0 当前整行 print $0
$1..$NF 第 N 个字段 print $1,$3
NF 当前行字段数 if (NF<5) print
NR 读到的总行号(全局) NR==1{print "header"}
FNR 当前文件内行号 多文件时常用
FS 输入分隔符 默认空白;可在 BEGIN 设置
OFS 输出分隔符 默认空格
RS 记录分隔符(行分隔) 可设置为空行或自定义分隔
ORS 输出记录分隔符 默认换行
FILENAME 当前文件名
ARGC/ARGV 参数个数/数组 脚本参数处理
ENVIRON 环境变量数组 ENVIRON["HOME"]
IGNORECASE 忽略大小写(gawk) IGNORECASE=1

4.4 内置函数小抄

  • 数字int() sqrt() rand() srand() atan2()
  • 字符串length() index(s,t) substr(s,i[,n]) split(s,a[,fs]) match(s,r) sub(r,s) gsub(r,s) gensub(r,s,how[,t])(gawk) tolower/ toupper
  • 数组(gawk):asort(a[,d]) 排序到数组;asorti(a[,d]) 按键排序
  • 时间(gawk):systime() strftime(fmt[,ts]) mktime()
  • IO/系统system(cmd) getline close() fflush()

4.5 高频场景与示例

(1) 选择列与重排格式

# 取第1、3列,并以逗号输出
awk -F'\t' -vOFS=, '{print $1,$3}' input.tsv > out.csv

(2) 条件筛选 + 聚合

# 只统计状态为 200 的字节数总和(NCSA 日志示例)
awk '$9==200 {sum+=$10} END{print sum}' access.log

(3) 分组聚合(按字段计数 TopN)

awk -F, '{cnt[$2]++} END{for(k in cnt) print cnt[k],k}' data.csv | sort -nr | head

(4) 使用正则匹配字段

awk -F: '$1 ~ /^(sys|daemon)/ {print $1,$3}' /etc/passwd

(5) 自定义记录分隔符(跨行记录)

# 以空行分隔记录(如 RFC-style headers)
awk -v RS='' '{print NR, length($0)}' mail.txt

(6) 生成简易报表(带表头、对齐)

awk -F, 'BEGIN{printf "%-20s %8s\n","City","Pop"}
{printf "%-20s %8d\n", $1, $2}' city.csv

5) sed —— 流编辑

5.1 命令行参数

选项 含义 备注
-n 安静模式(不自动打印) 需要显式 p 打印
-e 'script' 添加一条脚本命令 可多次叠加
-f script.sed 从文件读取脚本 复杂场景推荐
-i[SUFFIX] 原地修改(可备份后缀) macOS 需 -i '' 无备份
-E / -r 使用 ERE 扩展正则 BSD/macOS 用 -E;GNU 两者皆可
-s 将每个文件视作独立流 多文件时避免跨文件状态影响
-u 尝试更少缓冲(兼容用途) GNU sed 多为兼容占位
-z 以 NUL 分隔记录 处理含换行的“记录”
-l N l 命令换行宽度 sed -n 'l' 可显示不可见字符

5.2 地址选择(选中要处理的行)

  • 单地址:N(第 N 行)、$(最后一行)、/regex/(匹配行)
  • 范围:addr1,addr2(如 10,20 / /start/,/end/ / 5,$
  • 步进:first~step(如 1~2 选奇数行)
  • 取反:addr!cmd(对不匹配的行执行 cmd)

5.3 编辑命令全集(常用)

命令 作用 示例
s/regex/repl/flags 替换 s/foo/bar/g 全局;s/a/A/2 第二处;I 忽略大小写
p 打印当前模式空间 常与 -n 配合只打印命中行
d 删除当前行(读下一行) 过滤
D 删除第一个换行前的内容并立即重新开始 多行编辑利器
q [CODE] 退出 sed 加速:匹配到即退出
Q [CODE] 立即退出,不打印模式空间
a\ text 追加文本 a\new line
i\ text 插入文本
c\ text 替换选择的整行
y/set1/set2/ 字符集替换(逐字符) ROT13 等
n 打印(若非 -n)并读下一行 与条件流结合
N 将下一行追加到模式空间(加入换行) 跨行编辑
h/H 覆盖/追加到保持空间 双缓冲技巧
g/G 从保持空间覆盖/追加到模式空间
x 交换保持空间与模式空间
= 打印当前行号
r file 读文件内容并插入(在当前行后)
w file 将当前行写入文件
: label / b label 创建标签 / 跳转 t label 为“若有替换则跳转”
l 可视化打印(转义不可见字符) -l 结合换行宽度

\ 在脚本文件中用于续行;交互命令中 a\/i\/c\ 后通常需换行输入正文。

5.4 高频场景与示例

(1) 原地批量替换(带备份)

# GNU/Linux:备份为 .bak;macOS:-i '' 表示不留备份
gnu_sed: sed -i.bak 's/old/new/g' *.conf
mac_sed: sed -i ''  's/old/new/g' *.conf

(2) 只替换匹配行的第一个 IP 为 [MASK]

sed '/ERROR/ s/\b\([0-9]{1,3}\.){3}[0-9]{1,3}\b/[MASK]/' app.log

(3) 删除区块(从 START 到 END)

sed '/^# START/,/^# END/d' config.ini

(4) 跨行合并:把以反斜杠续行的两行拼成一行

# 将结尾为 \ 的行与下一行合并(去掉续行符)
sed -e ':a' -e '/\\$/ N; s/\\\n//; ta' file

(5) 仅打印包含 foo 的行的下一行

sed -n '/foo/{n;p}' file

(6) 复杂脚本文件示例(多规则)

# clean.sed
# 1) 去掉注释与空行
/^[[:space:]]*#/d
/^[[:space:]]*$/d
# 2) 规范等号两侧空白
s/[[:space:]]*=[[:space:]]*/ = /g
# 3) key 全部小写
s/^\([A-Za-z_][A-Za-z0-9_]*\)/\L\1/

运行:sed -f clean.sed input.conf > output.conf


6) 组合技:管道与分工

(1) 先筛后算

# 统计 5xx 状态的请求体字节和
grep -E "\s5[0-9]{2}\s" access.log | awk '{sum+=$10} END{print sum}'

(2) 先改再算

# 将大小写不敏感的 ERROR/WARN 统一为大写再按级别计数
sed -E 's/(error|warn)/\U\1/g' app.log | awk '{cnt[$1]++} END{for(k in cnt)print k,cnt[k]}'

(3) 结构化提取 + 仅值输出

# 提取配置中所有端口号(去重、排序)
grep -RhoE '\bport[[:space:]]*=[[:space:]]*[0-9]+\b' conf/ |
  sed -E 's/.*=\s*([0-9]+)/\1/' |
  sort -n | uniq

7) 兼容性与性能优化

  • GNU vs. BSD/macOS

    • sed -i:GNU 可省后缀,macOS 需 -i '' 才不留备份。
    • sed -r:GNU 可用,macOS 无此选项,用 -E
    • grep -P:PCRE 在部分系统未编译;可用 perl -ne 'print if /.../' 代替。
    • 一些 --exclude* 选项在 BSD grep 缺失,用 find ... -exec 组合。
  • 速度优化

    • 确定是字面量匹配:grep -F 更快
    • 降低本地化开销:LC_ALL=C 可显著加速纯 ASCII 文本处理。
    • 大目录递归:用 ripgrep (rg) 可更快;或 xargs -P 并行。
    • awk 中减少管道与 system() 调用;汇总放在 END{}
  • 健壮性

    • 处理“奇怪文件名”用 NUL 分隔:grep -Zxargs -0find -print0
    • 正则含 {} 在 BRE 需转义;在 ERE 则不需。
    • 批量改文件建议先备份或用 git 追踪。

8) 练习题(附思路)

  1. 统计 NCSA 日志中各 IP 命中数 Top 10
    思路awk '{cnt[$1]++} END{for(k in cnt) print cnt[k],k}' | sort -nr | head

  2. .env 文件里 KEY=value 的 value 去掉两端空白并小写
    思路sed -E 's/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/\1=\L\2/' .env

  3. 在源码中查找 TODO,并显示文件名、行号与前后 1 行上下文
    思路grep -RIn -C1 'TODO' src/

  4. 从 CSV 第 2 列提取域名,统计不同后缀 .com/.net/.org 数量
    思路awk -F, '{split($2,a,"."); t=a[length(a)]; cnt[t]++} END{for(k in cnt)print k,cnt[k]}' file.csv

  5. 合并以反斜杠续行的配置,删除注释与空行
    思路:用 sedN; s/\\\n// 合并,再用地址与正则删除注释/空白。


附:常见错误排查

  • sed: RE error: illegal byte sequence:设置 LC_ALL=C 或统一输入编码。
  • grep: binary file matches:加 -a-I
  • awk: field separator 被正则特殊含义影响:用字符类或转义,如 -F'[|]'
  • macOS sed -i 报错:需 -i ''

温馨提示:不同发行版/版本参数可能略有差异,man grep / man sed / man awk--help 为准。本文覆盖 GNU 常见参数与实践套路,满足日常到生产的大多数需求。

posted @ 2025-09-06 20:11  kyle_7Qc  阅读(92)  评论(0)    收藏  举报