Linux命令行与脚本-04-文本处理利器:sed、awk与正则表达式
全文摘要
本文将带你深入理解Linux下的文本处理工具,帮助你掌握sed、awk和正则表达式的强大功能。你将学到正则表达式的语法规则、sed流编辑器的使用技巧、awk编程语言的核心概念、以及如何组合使用这些工具处理复杂的文本数据。通过阅读本文,你将拥有处理日志文件、批量修改配置文件、提取分析数据的强大能力。
全书总结
文本处理是Linux系统管理的必备技能,sed、awk和grep是文本处理的”三剑客”。本文系统梳理了正则表达式的基础语法与高级模式、sed的查找替换与文本转换、awk的模式匹配与数据处理、以及grep/egrep/fgrep的使用差异。从简单的文本替换到复杂的数据提取,涵盖了文本处理的完整技能树。适合系统运维、数据处理工程师、日志分析人员、以及对文本自动化处理感兴趣的技术人员阅读。
一、正则表达式:模式匹配的语言
正则表达式是描述文本模式的强大语言,是sed、awk、grep等工具的基础。
flowchart TB subgraph RE[正则表达式基础] direction TB Literal[字面字符<br/>abc 匹配 "abc"] Dot[点号 .<br/>匹配任意单个字符] CharSet[字符集 [abc]<br/>匹配 a/b/c] Negate[^0-9]<br/>[br/>非数字] Anchor[锚点<br/>^行首 $行尾] end subgraph Quantifiers[量词] direction TB Star[*<br/>0次或多次] Plus[+<br/>1次或多次] Quest[?<br/>0次或1次] Range[{n,m}<br/>n到m次] end subgraph Advanced[高级模式] direction TB Group[捕获组 (...)<br/>保存匹配] Ref[反向引用 \n<br/>引用第n组] Bound[单词边界 \b<br/>单词边界] Back[后退查找 (?<=...)<br/>向前/向后查找] end RE --> Practice[实战应用] Quantifiers --> Practice Advanced --> Practice style Dot fill:#e3f2fd style Star fill:#fff9c4 style Group fill:#c8e6c9
图表讲解:这张图展示了正则表达式的核心概念——正则表达式是文本处理的瑞士军刀。
基础元字符:
.:匹配任意单个字符(除换行符外)[abc]:字符集,匹配a、b或c中的任意一个[^0-9]:否定字符集,匹配非数字的字符^:匹配行首$:匹配行尾\d:匹配数字(等同于[0-9])\w:匹配单词字符(字母、数字、下划线)\s:匹配空白字符(空格、制表符、换行符)
量词:
*:匹配前导模式0次或多次+:匹配前导模式1次或多次?:匹配前导模式0次或1次{n}:匹配恰好n次{n,m}:匹配n到m次
示例:
^[A-Z]:以大写字母开头的行.*\.log$:以.log结尾的文件名\berror\b:单词”error”(前后是单词边界,不会匹配”errornous”中的error)192\.168\.[0-9]+:匹配IP地址(如192.168.1.1)
二、sed:流编辑器
sed(Stream Editor)是强大的文本处理工具,用于查找和替换文本。
flowchart TB subgraph SedOps[sed操作模式] direction TB Sub[替换<br/>s/old/new/] Del[删除<br/>2d 删除第2行] Print[打印<br/>/pattern/p 打印匹配行] App[追加<br/>a\文本] Ins[插入<br/>i\文本] end subgraph SedInPlace[原地修改] direction TB Back[备份<br/>-i.bak] Direct[直接修改<br/>-i] end subgraph SedAdv[sed高级用法] direction TB Addr[地址范围<br/>1,10p 1到10行] Cmd[多命令<br/>-e 'cmd1' -e 'cmd2'] Read[读取文件<br/>r file] Write[写入文件<br/>w file] end SedOps --> Tool[文本处理] SedInPlace --> Tool SedAdv --> Tool style Sub fill:#e3f2fd style Del fill:#ffcdd2 style Print fill:#c8e6c9 style Read fill:#ba68c8
图表讲解:这张图展示了sed的核心操作和高级功能——sed是批量文本修改的神器。
基本替换:
# s/old/new/ 替换第一个匹配
sed 's/foo/bar/' file.txt
# s/old/new/g 全局替换
sed 's/foo/bar/g' file.txt
# 删除行
sed '/pattern/d' file.txt # 删除匹配的行
sed '5d' file.txt # 删除第5行
sed '/foo/,/bar/d' file.txt # 删除foo和bar之间的行常用选项:
-n:默认打印所有行,-n取消自动打印-i:原地修改文件(小心使用!)-i.bak:修改前备份为file.bak-e:指定多个命令
组合使用:
# 删除空行
sed '/^$/d' file.txt
# 删除注释行
sed '/^#/d' file.txt
# 替换并删除空行
sed -e 's/foo/bar/' -e '/^$/d' file.txt高级用法:
# 地址范围:只处理5到10行
sed '5,10s/foo/bar/' file.txt
# 读入文件内容
sed '/pattern/r insert.txt' file.txt
# 写入到文件
sed 'w output.txt' file.txt三、awk:模式扫描与数据处理语言
awk不仅是文本处理工具,更是一门完整的编程语言。
flowchart TB subgraph AwkFlow[awk执行流程] direction TB Begin[BEGIN块<br/>初始化] Input[逐行读取<br/>处理每一行] Action[模式匹配<br/>执行动作] End[END块<br/>清理工作] end subgraph AwkFeat[awk核心特性] direction TB FS[字段分隔符<br/>-F:指定分隔符] NF[字段数量<br/>$0整行 $NF最后字段] NR[行号<br/>FNR当前文件行号] Print[格式化输出<br/>printf] end subgraph AwkAdv[awk高级特性] direction TB Arr[数组<br/>关联数组] Func[函数<br/>自定义函数] Sys[系统调用<br/>getline system] end AwkFlow --> Use[数据处理] AwkFeat --> Use AwkAdv --> Use style Begin fill:#e3f2fd Action fill:#fff9c4 End fill:#c8e6c9
图表讲解:这张图展示了awk的执行模型和核心特性——awk是数据提取和分析的利器。
基本语法:
# 打印每一行
awk '{print $0}' file.txt
# 打印第1和第3字段
awk '{print $1, $3}' file.txt
# 模式匹配后打印
awk '/error/ {print $0}' file.txtBEGIN和END:
# BEGIN在处理前执行,END在处理后执行
awk 'BEGIN {print "Start"} {print $0} END {print "End"}' file.txt常用内置变量:
NR:当前行号NF:字段数量$0:整行$1, $2, ..., $NF:第1、2…、最后一个字段FS:输入字段分隔符(默认是空格)OFS:输出字段分隔符(默认是空格)
实用例子:
# 统计文件行数
awk 'END {print NR}' file.txt
# 打印字段数大于3的行
awk 'NF > 3 {print $0}' file.txt
# 计算数字列的和
awk '{sum += $1} END {print sum}' numbers.txt
# 提取特定字段的行
awk -F: '$1 == "root" {print $0}' /etc/passwd
# 格式化输出
awk '{printf "%-10s %5d\n", $1, $2}' file.txt四、grep:文本搜索工具
grep是查找文本模式的利器,与sed、awk并称为”三剑客”。
flowchart TB subgraph GrepVars[grep变体] direction TB Grep[grep<br/>基础grep] Egrep[egrep<br/>扩展grep<br/>等同于grep -E] Fgrep[fgrep<br/>固定字符串<br/>不解释正则] Zgrep[zgrep<br/>压缩文件搜索] end subgraph GrepOpts[grep常用选项] direction TB I[i<br/>忽略大小写] V[v<br/>反向查找] Invert[n/v<br/>显示不匹配的行] Count[c<br/>只计数] Num[n<br/>显示行号] Cntxt[C n<br/>显示上下n行] Color[color<br/>高亮匹配] Recur[R<br/>递归搜索目录] end subgraph GrepOut[grep输出格式] CountOut[c<br/>只输出匹配数量] Line[n<br/>显示行号] Name[H<br/>只显示文件名] Match[o<br/>只输出匹配部分] end GrepVars --> GrepOpts GrepOpts --> GrepOut style Grep fill:#e3f2fd style I fill:#c8e6c9 style V fill:#ba68c8
图表讲解:这张图展示了grep的变体和常用选项——grep是日常查找文本的首选工具。
grep基础:
# 搜索包含"error"的行
grep "error" file.txt
# 忽略大小写搜索
grep -i "ERROR" file.txt
# 反向查找(不匹配的行)
grep -v "comment" file.txt
# 递归搜索目录
grep -r "TODO" /path/to/dir常用组合:
# 搜索并显示行号
grep -n "pattern" file.txt
# 显示匹配行及上下2行
grep -C 2 "error" file.txt
# 只显示文件名(不显示匹配行)
grep -l "pattern" /path/to/dir/*
# 统计匹配次数
grep -c "error" file.txt
# 只输出匹配部分(不显示整行)
grep -o "[0-9]\+" file.txt # 提取所有数字egrep和fgrep:
egrep等同于grep -E,使用扩展正则表达式(如+、?、|不需要转义)。fgrep等同于grep -F,固定字符串搜索(不解释正则,速度更快)。
管道组合:
# 查找进程
ps aux | grep nginx
# 查找文件并显示行号
grep -n "pattern" file.txt | less
# 查找并计数
ps aux | grep -c python
# 组合多个grep
grep "ERROR" log.txt | grep -v "DEBUG"五、文本处理实战案例
结合sed、awk、grep解决实际问题。
flowchart TB subgraph Tasks[常见文本处理任务] direction TB T1[批量替换<br/>配置文件修改] T2[日志分析<br/>提取错误信息] T3[数据提取<br/>CSV/JSON处理] T4[文件合并<br/>多文件处理] T5[格式转换<br/>日志格式标准化] end subgraph Tools[工具选择] direction TB Replace[任务1<br/>sed最合适] Search[任务2<br/>grep+awk] Extract[任务3<br/>awk最适合] Merge[任务4<br/>cat+awk] Convert[任务5<br/>sed+awk] end Tasks --> Tools --> Result[高效处理] style Replace fill:#e3f2fd style Search fill:#fff9c4 style Extract fill:#c8e6c9
图表讲解:这张图展示了文本处理任务和工具选择——选择合适的工具是高效处理的关键。
案例1:批量修改配置文件
# 将所有配置文件的旧域名替换为新域名
sed -i 's/old.domain.com/new.domain.com/g' *.conf
# 注释掉包含"DEBUG"的行
sed -i 's/^DEBUG/#DEBUG/' app.conf案例2:分析Web服务器日志
# 统计访问IP的数量
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 提取404错误的请求
awk '$9 == 404 {print $7}' access.log > 404_urls.txt
# 计算平均响应时间
awk '{sum+=$10; count++} END {print sum/count}' access.log案例3:CSV数据处理
# 提取特定列
awk -F, '{print $1, $3}' data.csv > output.txt
# 计算某列的平均值
awk -F, 'NR>1 {sum+=$2; count++} END {print sum/count}' data.csv案例4:查找特定模式的日志
# 查找今天的错误日志
grep "$(date +%Y-%m-%d).*ERROR" /var/log/app.log
# 查找特定用户的操作日志
grep "user=admin" /var/log/auth.log
# 查找包含IP地址的行
grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' file.txt六、工具组合与脚本集成
将sed、awk、grep组合成强大的文本处理管道。
flowchart LR Input[输入数据<br/>文件/管道] --> Grep[grep<br/>过滤数据] Grep --> Sed[sed<br/>清洗数据] Sed --> Awk[awk<br/>提取计算] Awk --> Output[输出结果<br/>文件/屏幕] Input -.->|直接处理| Awk style Input fill:#e3f2fd style Grep fill:#fff9c4 style Sed fill:#c8e6c9 style Awk fill:#ba68c8 style Output fill:#a5d6a7
图表讲解:这张图展示了文本处理管道的典型流程——工具组合产生1+1>2的效果。
管道组合示例:
# 查找进程、提取PID、杀死进程
ps aux | grep nginx | awk '{print $2}' | xargs kill -9
# 分析日志:提取错误行、提取IP、统计访问次数
grep ERROR /var/log/app.log | awk '{print $5}' | sort | uniq -c | sort -rn
# 处理CSV:跳过标题行、提取字段、计算平均值
tail -n +2 data.csv | awk -F, '{sum+=$2; count++} END {print sum/count}'
# 多文件处理:查找所有.log文件、提取特定行、合并结果
cat *.log | grep "ERROR" | awk '{print $1,$2}' > errors.txt脚本集成:
#!/bin/bash
# 日志分析脚本
LOGFILE="/var/log/app.log"
DATE=$(date +%Y-%m-%d)
OUTPUT="report_$DATE.txt"
echo "Error Report - $DATE" > $OUTPUT
echo "=====================" >> $OUTPUT
echo "" >> $OUTPUT
# 提取今天的错误日志
grep "$(date +%Y-%m-%d).*ERROR" $LOGFILE | while read line; do
# 提取时间戳、错误级别、错误消息
timestamp=$(echo "$line" | awk '{print $1,$2}')
message=$(echo "$line" | cut -d']' -f3-)
echo "$timestamp $message" >> $OUTPUT
done
echo "Total errors: $(grep "$(date +%Y-%m-%d).*ERROR" $LOGFILE | wc -l)" >> $OUTPUT结语
sed、awk、grep是Linux文本处理的”三剑客”,掌握它们能让你的文本处理效率提升数量级。
工具选择指南:
- grep:查找包含特定模式的行,适合过滤日志、搜索代码
- sed:查找和替换文本,适合批量修改配置文件、删除/添加行
- awk:结构化数据处理,适合提取字段、计算统计、格式化输出
学习路径:
- 先掌握正则表达式基础,它是所有工具的基础
- 学习grep的基本用法,这是最常用的
- 学习sed的替换功能,解决常见的文本修改需求
- 最后学习awk,它是一门完整的编程语言
实践建议:
- 在测试文件上验证命令,再用于重要文件
- 使用
-i前先检查备份 - 复杂的脚本分段调试,逐步构建
- 善用管道,让工具各司其职
掌握文本处理工具后,你会发现很多之前需要手工操作的任务,一条命令就能搞定。这就是Linux命令行的威力。
常见问题解答
Q1:sed和awk有什么区别,什么时候用哪个?
答:sed是流编辑器,适合简单的查找替换、删除、插入等操作。awk是编程语言,适合字段提取、计算、格式化输出等复杂任务。
经验法则:如果你只需要”修改”文本(替换、删除、添加行),用sed;如果你需要”分析”文本(提取字段、计算统计、格式化输出),用awk。
例如:sed 's/old/new/g' file(替换)vs awk '{sum+=$1} END {print sum}' file(计算)。两者可以组合使用:先grep过滤,再sed替换,最后awk分析。在管道中,它们各司其职,配合完成复杂的文本处理任务。
Q2:正则表达式中的.*和.*?有什么区别?
答:.*是贪婪模式,匹配尽可能多的字符;.*?是非贪婪模式,匹配尽可能少的字符。
在"foo.*bar"中,.匹配任意字符,*是量词(0次或多次)。对于文本”fooXXXbarYYYbar”,foo.*bar匹配”fooXXXbarYYYbar”(到最后的bar),foo.*?bar匹配”fooXXXbar”(到第一个bar)。
贪婪模式可能”匹配过多”,导致结果不符合预期。非贪婪模式更适合提取特定模式之间的内容。例如,提取HTML标签内容时,<p>.*?</p>匹配单个段落(而不是到
Q3:如何在awk中处理多个分隔符?
答:awk默认使用单个字符作为字段分隔符(FS)。如果字段分隔符可能包含多个字符(如::或::),或需要动态分隔符,可以用几种方法:(1)-F'::'指定多个字符为分隔符:awk -F'::' '{print $1, $3}'(冒号分隔)。注意-F指定的分隔符被当作字符串,需要引号。
(2)使用split()函数在行内动态分割:awk '{split($0, a, ":"); print a[1], a[3]}'。(3)用正则表达式作为字段分隔符:awk -F'[: ]+' '{print $1, $3}'(冒号和空格都是分隔符)。
对于CSV等格式,字段内包含分隔符的情况,需要更复杂的处理(如去除引号后分割)。
Q4:如何在awk中处理多个文件?
答:awk可以处理多个文件,文件名存储在FILENAME变量中,行号在每个文件内从1开始(FNR),全局行号持续增加(NR):
这会在每行前加上文件名和行号。
可以在BEGIN块中初始化,在END块中汇总:
awk '{count[FILENAME]++} END {for (f in count) print f, count[f]}' *.txt这会统计每个文件的行数。FNR==1 {print "Processing: " FILENAME}可以检测文件边界,在新文件开始时执行操作。
Q5:如何提高大文件的处理速度?
答:大文件处理需要考虑性能优化:
- 减少awk的字段分割:
awk '{print $1}'比awk '{print $1, $3}'快(后者需要分割更多字段)。 - 提前过滤:用grep过滤掉不需要的行,再交给awk处理:
grep pattern file | awk '{...}'。 - 使用更快的工具:对于简单计数,
grep -c比awk '{count++} END {print count}'快。对于查找,grep -f(固定字符串)比正则表达式快。 - 减少内存使用:不使用数组存储大量数据(除非必要),边读边处理。
- 使用LC_ALL=C:设置locale为C,加速排序和字符串比较:
LC_ALL=C sort file。 - 分块处理:对于超大文件,可以分割处理:
split -l 1000000 large_file.txt分成小文件,逐个处理,最后合并结果。 - 使用mawk:mawk是awk的现代实现,性能更好,支持更大的文件和更多特性。