🔪 Day 5 of 7

文本处理三剑客

📖 预计时长:3 小时 🎯 难度:中高级
grep / sed / awk 是 Linux 文本处理的核心三工具,运维日常80%的文本分析工作都靠它们完成。掌握这三剑客,日志分析、配置修改、数据提取都不在话下。
基础正则表达式 grep 过滤与模式匹配 sed 替换与编辑 awk 字段处理 awk BEGIN/END 三剑客组合管道

🔍 grep

Global Regular Expression Print
按模式过滤行,擅长搜索和过滤文本

✂️ sed

Stream Editor
流式编辑,擅长替换、删除、插入行内容

📊 awk

Aho Weinberger Kernighan
字段分析,擅长结构化数据提取和计算

1️⃣ 正则表达式基础

符号含义示例
.匹配任意单个字符(换行除外)a.c → abc/a1c
*前面字符出现0次或多次ab*c → ac/abc/abbc
+前面字符出现1次或多次(ERE)ab+c → abc/abbc
?前面字符出现0或1次(ERE)ab?c → ac/abc
^行首^error → error开头的行
$行尾error$ → error结尾的行
[]字符集合[aeiou] → 任意小写元音
[^]排除字符集合[^0-9] → 非数字
\b单词边界\bword\b
{n,m}出现n到m次(ERE)[0-9]{1,3}

2️⃣ grep — 文本过滤利器

# 基本用法
grep "error" /var/log/syslog         # 过滤含 error 的行
grep -i "error" /var/log/syslog      # 不区分大小写
grep -n "error" /var/log/syslog      # 显示行号
grep -v "error" /var/log/syslog      # 反向:不包含 error 的行
grep -c "error" /var/log/syslog      # 统计匹配行数
grep -l "error" /var/log/*.log        # 只显示匹配的文件名
grep -r "error" /var/log/             # 递归搜索目录
grep -A3 "error" /var/log/syslog     # 显示匹配行及后3行
grep -B3 "error" /var/log/syslog     # 显示匹配行及前3行
grep -C3 "error" /var/log/syslog     # 显示匹配行及前后3行
bash
# -E 扩展正则(ERE)
grep -E "error|warning|critical" /var/log/syslog
grep -E "^[0-9]{4}-[0-9]{2}-[0-9]{2}" app.log

# -P Perl 兼容正则(功能最强)
grep -P "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" access.log  # 提取IP

# -o 只输出匹配部分
grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" access.log

# --color=always 高亮显示
grep --color=always "error" /var/log/syslog

# 实用:查找所有 failed 登录
grep -i "failed" /var/log/auth.log | grep "ssh"
bash

3️⃣ sed — 流编辑器

# 替换:s/旧/新/标志
sed 's/error/ERROR/' file.txt        # 每行第一个
sed 's/error/ERROR/g' file.txt       # 全局替换
sed 's/error/ERROR/gi' file.txt      # 全局+忽略大小写
sed 's/error/ERROR/2' file.txt       # 替换每行第2个

# -i 原地修改文件(重要!)
sed -i 's/old/new/g' config.conf
sed -i.bak 's/old/new/g' config.conf  # 备份为 .bak 再修改

# 删除行
sed '/^#/d' config.conf              # 删除注释行
sed '/^$/d' config.conf              # 删除空行
sed '5d' file.txt                    # 删除第5行
sed '2,5d' file.txt                  # 删除2到5行
sed '$d' file.txt                    # 删除最后一行

# 打印(需要 -n 关闭默认输出)
sed -n '5p' file.txt                 # 打印第5行
sed -n '2,5p' file.txt               # 打印2到5行
sed -n '/error/p' file.txt           # 打印含 error 的行
bash
# 插入与追加
sed '3i\新插入的行' file.txt          # 在第3行前插入
sed '3a\新追加的行' file.txt          # 在第3行后追加
sed '/pattern/a\追加到匹配行后' file.txt

# 多个操作用 -e 或分号
sed -e '/^#/d' -e '/^$/d' config.conf
sed '/^#/d; /^$/d' config.conf

# 实用:修改配置文件某参数
sed -i 's/^#\(PermitRootLogin\).*/\1 no/' /etc/ssh/sshd_config
# 使用变量
port=8080
sed -i "s/^Port.*/Port $port/" /etc/ssh/sshd_config
bash

4️⃣ awk — 文本分析引擎

awk 将每行文本按分隔符切分为字段:$1 第1字段,$2 第2字段,$NF 最后一个字段,$0 整行。

# 基本语法:awk 'pattern { action }' 文件

# 打印特定字段
awk '{print $1, $3}' /etc/passwd      # 用默认分隔符(空白)
awk -F: '{print $1, $3}' /etc/passwd  # 用:作为分隔符

# 内置变量
# NR = 当前行号    NF = 字段总数
# FS = 输入分隔符  OFS = 输出分隔符
# RS = 记录分隔符  ORS = 输出记录分隔符
# FILENAME = 文件名

awk -F: '{print NR, $1, NF}' /etc/passwd
awk 'NR>=2 && NR<=5 {print}' file.txt   # 打印2到5行
awk 'NF > 3 {print}' file.txt            # 字段数大于3的行
bash
# BEGIN 和 END 块
awk 'BEGIN { print "开始处理" }
     { sum += $1; count++ }
     END   { printf "总和:%d  平均:%.2f\n", sum, sum/count }
' data.txt

# 条件过滤
awk -F: '$3 >= 1000 {print "普通用户:", $1}' /etc/passwd

# 统计 HTTP 状态码
awk '{print $9}' access.log | sort | uniq -c | sort -rn

# 格式化输出
awk -F: '{ printf "%-15s UID: %d\n", $1, $3 }' /etc/passwd

# 多分隔符
awk -F'[,:]' '{print $1}' file.txt  # 逗号或冒号分隔
bash
# awk 中的数组
# 统计各 IP 访问次数
awk '{ip[$1]++} END { for(i in ip) print ip[i], i }' access.log | sort -rn

# awk 控制流
awk '{
    if ($1 > 100) {
        print "大值:", $1
    } else {
        print "小值:", $1
    }
}' data.txt

# awk 实用:提取 nginx 日志慢请求
awk '$NF > 1.0 { print $0 }' nginx_access.log
bash

5️⃣ 三剑客组合实战

#!/bin/bash
# 实战:分析 nginx 访问日志

LOG="/var/log/nginx/access.log"
[[ ! -f "$LOG" ]] && { echo "日志文件不存在"; exit 1; }

echo "===== Nginx 访问日志分析 ====="

# 1. 总请求数
echo "总请求数:$(wc -l < "$LOG")"

# 2. 状态码分布
echo "--- 状态码分布 ---"
awk '{print $9}' "$LOG" | sort | uniq -c | sort -rn

# 3. 访问量 Top 10 IP
echo "--- Top 10 IP ---"
awk '{print $1}' "$LOG" | sort | uniq -c | sort -rn | head -10

# 4. 404 请求路径
echo "--- 404 路径 ---"
grep ' 404 ' "$LOG" | awk '{print $7}' | sort | uniq -c | sort -rn | head -10

# 5. 每分钟请求量(最近10分钟)
echo "--- 最近请求速率 ---"
grep "$(date '+%d/%b/%Y:%H:%M' --date='10 minutes ago')" "$LOG" | wc -l
bash

🏋️ 今日练习

  1. 用 grep 从 /var/log/auth.log 提取所有 SSH 登录失败的 IP 地址,去重排序并统计次数
  2. 用 sed 清理一个配置文件:去除注释行、空行,并将所有 #号注释都删掉
  3. 用 awk 分析 /etc/passwd,输出 UID 大于 1000 的用户的用户名、UID 和 Shell,用制表符对齐
  4. 用管道组合:统计 /var/log/syslog 中每个小时出现 error 的次数,格式:HH: N 次
  5. 写一个脚本,用 awk 计算 CSV 文件某列数值的总和、平均值、最大值和最小值