CentOS Stream 9 的过滤器 ------ 语法详解与实战案例
系统环境:CentOS Stream 9 / RHEL 9
核心工具:grep, sed, awk, cut, wc, sort, uniq, Perl
目标:掌握 Linux 文本处理全流程,从基础过滤到高级编程
一、简单过滤器(基础文本处理工具)
1.1 cat ------ 连接并显示文件内容
✅ 语法:
bash
cat [选项] [文件...]
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-n |
显示行号 |
-b |
显示非空行的行号 |
-s |
压缩连续空行 |
-A |
显示所有字符(包括不可见字符) |
✅ 示例:
bash
# 创建测试文件
echo -e "Line 1\n\nLine 3\n \nLine 5" > test.txt
# 显示内容(默认)
cat test.txt
# 显示行号(包括空行)
cat -n test.txt
# 输出:
# 1 Line 1
# 2
# 3 Line 3
# 4
# 5 Line 5
# 压缩空行并显示非空行号
cat -sb test.txt
# 输出:
# 1 Line 1
# 2 Line 3
# 3 Line 5
1.2 head / tail ------ 查看文件开头/结尾
✅ 语法:
bash
head -n N file # 显示前N行
tail -n N file # 显示后N行
tail -f file # 实时追踪文件末尾(日志监控)
✅ 示例:
bash
# 显示前3行
head -n 3 /etc/passwd
# 显示后5行
tail -n 5 /var/log/messages
# 实时监控日志(按 Ctrl+C 退出)
tail -f /var/log/secure
1.3 nl ------ 添加行号(比 cat -n 更灵活)
✅ 语法:
bash
nl [选项] [文件]
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-b a |
所有行编号(包括空行) |
-b t |
只对文本行编号(默认) |
-n ln |
行号左对齐 |
-w N |
行号宽度为N |
✅ 示例:
bash
nl -b a -n ln -w 3 test.txt
# 输出:
# 1 Line 1
# 2
# 3 Line 3
# 4
# 5 Line 5
1.4 tac ------ 反向显示文件(从最后一行开始)
✅ 语法:
bash
tac [文件]
✅ 示例:
bash
echo -e "A\nB\nC" > abc.txt
tac abc.txt
# 输出:
# C
# B
# A
1.5 rev ------ 反转每行字符
✅ 语法:
bash
rev [文件]
✅ 示例:
bash
echo "Hello World" | rev
# 输出:dlroW olleH
# 反转 /etc/passwd 每行(用于密码逆向分析?慎用)
rev /etc/passwd | head -n 3
1.6 wc ------ 统计行数、字数、字符数
✅ 语法:
bash
wc [选项] [文件]
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-l |
行数 |
-w |
单词数 |
-c |
字节数 |
-m |
字符数(支持UTF-8) |
✅ 示例:
bash
wc -l /etc/passwd # 统计行数
wc -w /etc/group # 统计单词数
wc -c /etc/hosts # 统计字节数
wc -m /etc/issue # 统计字符数(含中文)
# 统计多个文件
wc -l *.log
1.7 sort ------ 排序
✅ 语法:
bash
sort [选项] [文件]
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-n |
按数值排序 |
-r |
逆序 |
-k N |
按第N列排序 |
-t C |
指定分隔符为C |
-u |
去重(需先排序) |
✅ 示例:
bash
# 按字母排序
echo -e "banana\napple\ncherry" | sort
# 输出:apple, banana, cherry
# 按数值排序
echo -e "10\n2\n100" | sort -n
# 输出:2, 10, 100
# 按第二列排序(: 分隔)
sort -t: -k3n /etc/passwd | head -n 5
1.8 uniq ------ 去除相邻重复行(必须先排序!)
✅ 语法:
bash
uniq [选项] [文件]
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-c |
统计重复次数 |
-d |
只显示重复行 |
-u |
只显示唯一行 |
✅ 示例:
bash
echo -e "A\nA\nB\nC\nC\nC" | uniq -c
# 输出:
# 2 A
# 1 B
# 3 C
# 统计登录失败IP(需先排序)
grep "Failed password" /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr
二、正则表达式(Regular Expressions)
正则表达式是文本处理的核心,广泛用于 grep/sed/awk/Perl 等工具。
2.1 基本元字符
| 元字符 | 说明 | 示例 |
|---|---|---|
. |
任意单字符 | a.c → abc, aXc |
^ |
行首 | ^root → 以 root 开头的行 |
$ |
行尾 | bash$ → 以 bash 结尾的行 |
* |
前字符0次或多次 | a* → "", "a", "aaa" |
[abc] |
字符集合 | [aeiou] → 匹配任意元音字母 |
[^abc] |
非字符集合 | [^0-9] → 非数字 |
✅ 示例:
bash
# 匹配以 # 开头的行(注释)
grep "^#" /etc/ssh/sshd_config
# 匹配以 .conf 结尾的行
grep "\.conf$" /etc/rsyslog.conf
2.2 特殊字符元字符(需转义)
| 元字符 | 说明 | 示例 |
|---|---|---|
\d |
数字 [0-9] | \d{3} → 三位数字 |
\w |
字母数字下划线 | \w+ → 单词 |
\s |
空白字符 | \s+ → 一个或多个空格/tab |
\b |
单词边界 | \broot\b → 匹配单词 root,不匹配 groot |
✅ 注意:在基础 grep 中需使用
\d等需-P参数(Perl 正则)或改用[0-9]
2.3 POSIX 字符类(兼容性好)
| 类 | 说明 | 等价 |
|---|---|---|
[:digit:] |
数字 | [0-9] |
[:alpha:] |
字母 | [a-zA-Z] |
[:alnum:] |
字母+数字 | [a-zA-Z0-9] |
[:space:] |
空白字符 | [ \t\n\r\f\v] |
[:upper:] |
大写字母 | [A-Z] |
[:lower:] |
小写字母 | [a-z] |
✅ 示例:
bash
# 匹配包含数字的行
grep "[[:digit:]]" /etc/passwd
# 匹配纯字母行
grep "^[[:alpha:]]*$" file.txt
2.4 重复量词(扩展正则)
| 量词 | 说明 | 示例 |
|---|---|---|
+ |
1次或多次 | a+ → "a", "aa" |
? |
0次或1次 | colou?r → color, colour |
{n} |
恰好n次 | \d{4} → 四位数字 |
{n,} |
至少n次 | \d{3,} → 至少三位数字 |
{n,m} |
n到m次 | \d{2,4} → 2~4位数字 |
✅ 需使用
grep -E或egrep
bash
# 匹配手机号(11位数字)
grep -E "^[1][3-9][0-9]{9}$" phones.txt
# 匹配IP地址(简化版)
grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log
2.5 位置匹配
| 锚点 | 说明 |
|---|---|
^ |
行首 |
$ |
行尾 |
\b |
单词边界 |
\B |
非单词边界 |
✅ 示例:
bash
# 匹配独立单词 root
grep "\broot\b" /etc/passwd
# 匹配行首空格
grep "^ " file.txt
2.6 子表达式和分支
| 语法 | 说明 |
|---|---|
(abc) |
子表达式(分组) |
| `a(b | c)d` |
✅ 示例:
bash
# 匹配 color 或 colour
grep -E "colou?r" file.txt # ? 方式
grep -E "col(o|ou)r" file.txt # 分支方式
# 提取括号内内容(需 sed/awk)
echo "User: (John Doe)" | sed -n 's/.*(\(.*\)).*/\1/p'
# 输出:John Doe
2.7 回调引用(反向引用)
使用
\1,\2... 引用前面的子表达式
✅ 示例:
bash
# 匹配重复单词(如 "the the")
echo "the the cat" | grep -E "\b([a-z]+) \1\b"
# 交换两个单词
echo "first second" | sed 's/\([a-z]*\) \([a-z]*\)/\2 \1/'
# 输出:second first
2.8 前后预查(零宽断言)
仅 Perl 正则支持(grep -P)
| 语法 | 说明 |
|---|---|
(?=exp) |
正向先行断言(后面是exp) |
(?!exp) |
负向先行断言(后面不是exp) |
(?<=exp) |
正向后行断言(前面是exp) |
(?<!exp) |
负向后行断言(前面不是exp) |
✅ 示例:
bash
# 匹配后面跟着 .conf 的单词(但不包含 .conf)
echo "nginx.conf apache.conf" | grep -oP '\w+(?=\.conf)'
# 输出:nginx, apache
# 匹配前面是 /etc/ 的文件名
echo "/etc/passwd /tmp/file" | grep -oP '(?<=/etc/)\w+'
# 输出:passwd
2.9 回调条件(条件匹配)
Perl 正则高级功能
perl
(?(condition)yes-pattern|no-pattern)
一般在 Perl 脚本中使用,shell 中较少用。
三、grep 正则表达式家族
3.1 grep 语法
✅ 基本语法:
bash
grep [选项] 模式 [文件...]
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-i |
忽略大小写 |
-v |
反向匹配(不包含) |
-n |
显示行号 |
-c |
统计匹配行数 |
-l |
只显示文件名 |
-r |
递归搜索目录 |
-E |
扩展正则(等价 egrep) |
-P |
Perl 正则(功能最强) |
-o |
只显示匹配部分 |
3.2 grep 实例
✅ 案例1:搜索配置文件中的非注释非空行
bash
grep -v "^#" /etc/ssh/sshd_config | grep -v "^$"
✅ 案例2:统计访问日志中 404 错误次数
bash
grep " 404 " /var/log/nginx/access.log | wc -l
✅ 案例3:提取所有IP地址
bash
grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log | sort | uniq -c
✅ 案例4:查找包含 root 或 admin 的行(忽略大小写)
bash
grep -iE "root|admin" /etc/passwd
✅ 案例5:查找前后有空格的单词 "error"
bash
grep -P "(?<=\s)error(?=\s)" app.log
四、sed 流编辑器
4.1 sed 语法
✅ 基本语法:
bash
sed [选项] '命令' 文件
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-n |
静默模式(只输出p命令内容) |
-i |
直接修改文件(慎用!) |
-e |
多命令 |
-f |
从脚本文件读取命令 |
4.2 sed 内部命令
| 命令 | 说明 | 示例 |
|---|---|---|
p |
打印 | sed -n '2p' file → 打印第2行 |
d |
删除 | sed '2d' file → 删除第2行 |
s/pattern/repl/flags |
替换 | sed 's/foo/bar/g' |
g |
全局替换 | s///g |
i |
插入行前 | 2i\New Line |
a |
插入行后 | 2a\New Line |
c |
替换整行 | 2c\New Content |
= |
打印行号 | sed '=' file |
! |
取反 | 1!d → 删除除第1行外所有行 |
4.3 sed 实例
✅ 案例1:替换文件中所有 "old" 为 "new"
bash
sed 's/old/new/g' file.txt
# -i 参数直接修改文件(备份建议)
sed -i.bak 's/old/new/g' file.txt
✅ 案例2:删除空行
bash
sed '/^$/d' file.txt
✅ 案例3:在匹配行后添加注释
bash
sed '/^Listen /a # This is the main port' /etc/httpd/conf/httpd.conf
✅ 案例4:提取括号内内容
bash
echo "Name: (John Doe), Age: (30)" | sed -n 's/.*(\([^)]*\)).*/\1/p'
# 输出:John Doe (只匹配第一个)
# 提取所有:
echo "Name: (John Doe), Age: (30)" | sed 's/[^()]*([^)]*)//g'
✅ 案例5:多命令处理
bash
sed -e 's/foo/bar/g' -e '/^$/d' -e '1i\# Modified on '"$(date)" file.txt
五、Perl 一行式命令
✅ 语法:
bash
perl -ne '命令' 文件 # 逐行处理
perl -pe '命令' 文件 # 逐行处理并打印
perl -i -pe '命令' 文件 # 直接修改文件
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-n |
循环读入每行(不自动打印) |
-p |
循环读入每行(自动打印) |
-i |
修改文件 |
-e |
执行命令 |
-l |
自动处理换行符 |
✅ 实例:
bash
# 替换(类似 sed)
perl -pe 's/foo/bar/g' file.txt
# 删除空行
perl -ne 'print unless /^$/' file.txt
# 提取IP(使用正则)
perl -ne 'while(/(\d+\.\d+\.\d+\.\d+)/g){print "$1\n"}' access.log
# 统计单词频次
perl -ne 'for $word (split) { $count{$word}++ } END { for $w (keys %count) { print "$w: $count{$w}\n" } }' file.txt
六、cut 抽取字段
✅ 语法:
bash
cut [选项] [文件]
✅ 常用选项:
| 选项 | 说明 |
|---|---|
-d C |
分隔符为C |
-f N |
第N个字段 |
-c N-M |
第N到M个字符 |
✅ 示例:
bash
# 提取用户名和shell(: 分隔)
cut -d: -f1,7 /etc/passwd
# 提取第1-5字符
cut -c1-5 /etc/hosts
# 提取第2列到最后
cut -d' ' -f2- data.txt
七、awk 编程
7.1 awk 脚本基本结构
✅ 语法:
bash
awk 'pattern { action }' file
✅ 完整结构:
awk
BEGIN { 初始化 }
pattern { 每行处理 }
END { 结束处理 }
7.2 awk 的工作流程
- 执行
BEGIN块(一次) - 逐行读入文件,匹配
pattern,执行action - 执行
END块(一次)
7.3 awk 内置变量(预定义变量)
| 变量 | 说明 |
|---|---|
$0 |
整行内容 |
$1,$2... |
第1,2...字段 |
NF |
字段数量 |
NR |
当前行号(全局) |
FNR |
当前文件行号 |
FS |
输入字段分隔符(默认空格) |
OFS |
输出字段分隔符(默认空格) |
RS |
输入记录分隔符(默认换行) |
ORS |
输出记录分隔符(默认换行) |
✅ 示例:
bash
# 打印行号和字段数
awk '{print NR, NF, $0}' /etc/passwd
# 打印最后一列
awk '{print $NF}' /etc/passwd
# 打印倒数第二列
awk '{print $(NF-1)}' /etc/passwd
7.4 自定义变量
bash
awk 'BEGIN{sum=0} {sum+=$1} END{print "Total:", sum}' data.txt
7.5 将外部变量值传递给 awk
✅ 方法1:使用
-v
bash
name="John"
awk -v user="$name" 'BEGIN{print "Hello, " user}'
✅ 方法2:作为参数(注意引号)
bash
awk 'BEGIN{print "Hello, " ENVIRON["USER"]}'
7.6 awk 运算与判断
✅ 支持:
+ - * / % > < >= <= == != && || !
✅ 示例:
bash
# 计算大于100的数值之和
awk '$1 > 100 {sum += $1} END{print sum}' data.txt
# 多条件判断
awk '$1 > 50 && $2 < 100 {print $0}' data.txt
7.7 设置字段分隔符
✅ 方法1:
-F
bash
awk -F: '{print $1}' /etc/passwd
✅ 方法2:在 BEGIN 中设置
FS
bash
awk 'BEGIN{FS=":"} {print $1}' /etc/passwd
✅ 方法3:正则分隔符
bash
awk -F'[ :]' '{print $1, $2}' file.txt # 空格或冒号分隔
7.8 流程控制语句
✅ if-else
bash
awk '{if($1 > 50) print "High"; else print "Low"}' data.txt
✅ for 循环
bash
awk 'BEGIN{for(i=1;i<=5;i++) print i}'
✅ while 循环
bash
awk 'BEGIN{i=1; while(i<=5) {print i; i++}}'
7.9 数组的应用
✅ 关联数组(key-value)
bash
# 统计每个用户的进程数
ps -ef | awk '{count[$1]++} END{for(user in count) print user, count[user]}'
# 排序输出(通过管道)
ps -ef | awk '{count[$1]++} END{for(user in count) print count[user], user}' | sort -nr
7.10 内置函数
✅ 字符串函数:
| 函数 | 说明 |
|---|---|
length(str) |
长度 |
substr(str, start, len) |
子串 |
index(str, sub) |
查找位置 |
tolower(str) |
转小写 |
toupper(str) |
转大写 |
✅ 数学函数:
| 函数 | 说明 |
|---|---|
int(x) |
取整 |
sqrt(x) |
平方根 |
rand() |
随机数 |
✅ 示例:
bash
# 提取域名
echo "https://www.example.com/path" | awk '{print substr($0, index($0, "//")+2)}' | awk -F/ '{print $1}'
# 转换大小写
awk '{print toupper($1)}' file.txt
八、综合性实战案例
案例1:Web 访问日志分析(access.log)
目标:统计访问IP、状态码、URL频次、流量TOP10
✅ 假设日志格式:
192.168.1.100 - - [16/Sep/2025:10:00:01 +0800] "GET /index.html HTTP/1.1" 200 1024
✅ 脚本:
analyze-access.sh
bash
#!/bin/bash
LOG="/var/log/nginx/access.log"
echo "=== TOP 10 IPs ==="
awk '{print $1}' "$LOG" | sort | uniq -c | sort -nr | head -10
echo -e "\n=== TOP 10 URLs ==="
awk '{print $7}' "$LOG" | sort | uniq -c | sort -nr | head -10
echo -e "\n=== Status Code Count ==="
awk '{print $9}' "$LOG" | sort | uniq -c | sort -nr
echo -e "\n=== TOP 10 Traffic (Bytes) ==="
awk '$10 ~ /^[0-9]+$/ {print $1, $7, $10}' "$LOG" | sort -k3nr | head -10
echo -e "\n=== 404 Errors ==="
grep " 404 " "$LOG" | awk '{print $7}' | sort | uniq -c | sort -nr
案例2:系统用户审计报告
目标:生成用户列表(UID>1000)、shell类型、家目录、最后登录
✅ 脚本:user-audit.sh
bash
#!/bin/bash
echo "User Audit Report - $(date)"
echo "========================================"
awk -F: '$3 >= 1000 && $3 != 65534 {
printf "%-15s %-10s %-20s %s\n", $1, $3, $6, $7
}' /etc/passwd
echo -e "\nShell Summary:"
cut -d: -f7 /etc/passwd | sort | uniq -c
echo -e "\nLast Login:"
last | awk 'NF>0 && $1 != "reboot" && $1 != "wtmp" {count[$1]++; lastseen[$1]=$4" "$5" "$6} END {
for(user in count) {
printf "%s: %d times, last: %s\n", user, count[user], lastseen[user]
}
}' | sort
案例3:自动化配置文件清理器
目标:删除注释和空行,保留有效配置
✅ 脚本:clean-config.sh
bash
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $0 <config_file>"
exit 1
fi
FILE="$1"
BACKUP="${FILE}.bak.$(date +%Y%m%d)"
# 备份原文件
cp "$FILE" "$BACKUP"
# 清理:删除空行和#开头的行(允许行内注释)
grep -v "^[[:space:]]*#" "$FILE" | grep -v "^[[:space:]]*$" > "$FILE.tmp"
# 使用 awk 保留行内注释(# 前有非空字符)
awk '
{
# 如果行内有 # 且前面有非空字符,则保留 # 前部分
if (match($0, /[^#[:space:]]+#/)) {
pos = RSTART + RLENGTH - 2 # # 前一个字符位置
print substr($0, 1, pos)
} else if ($0 !~ /^[[:space:]]*#/) {
print $0
}
}
' "$FILE.tmp" > "$FILE"
rm "$FILE.tmp"
echo "Cleaned: $FILE (backup: $BACKUP)"
案例4:日志实时监控 + 告警(awk + grep + mail)
✅ 脚本:
monitor-log.sh
bash
#!/bin/bash
LOG="/var/log/messages"
KEYWORDS=("ERROR" "CRITICAL" "FAILED" "DENIED")
EMAIL="admin@example.com"
tail -f "$LOG" | while read line; do
for kw in "${KEYWORDS[@]}"; do
if echo "$line" | grep -q "$kw"; then
echo "[$(date)] $kw detected: $line" | mail -s "ALERT: $kw in $LOG" "$EMAIL"
break
fi
done
done
✅ 后台运行:
bash
nohup ./monitor-log.sh > /tmp/monitor.log 2>&1 &
✅ 本章小结 & 最佳实践
- 组合使用 :
grep | sort | uniq | awk是黄金组合 - 正则优先 :复杂匹配用
-P(Perl 正则) - 安全第一 :
sed -i前先备份,或使用-i.bak - 性能考虑:大文件避免多次读取,用 awk 一次处理
- 可读性:复杂 awk 脚本写成文件,加注释
- 调试技巧 :
awk '{print NR, $0}'调试行号 - 编码注意 :处理中文用
LC_ALL=C.UTF-8
📚 附录:命令速查表
| 功能 | 命令 |
|---|---|
| 显示文件 | cat -n file |
| 前10行 | head -n 10 file |
| 后10行 | tail -n 10 file |
| 排序去重 | `sort file |
| 提取列 | cut -d: -f1 /etc/passwd |
| 替换文本 | sed 's/old/new/g' file |
| 匹配IP | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' file |
| 统计字段和 | awk '{sum+=$1} END{print sum}' file |
| 传递变量 | awk -v var="$shell_var" '...' |
🧩 扩展项目建议
- 日志分析仪表盘:awk + gnuplot 生成图表
- 配置文件版本对比:diff + awk 高亮差异
- 自动化报表系统:cron + awk + mail
- 入侵检测脚本:监控日志 + 自动封IP
- 数据清洗管道:grep + sed + awk + sort + uniq
这份文档覆盖了 CentOS Stream 9 文本过滤器的全部核心知识点 + 语法细节 + 配置说明 + 实用脚本 + 综合项目,所有配置和代码均含详细注释,可直接用于教学、自学或生产环境参考。