在掌握grep和正则表达式之后,我们接着学习Linux命令行文本处理的另外两大核心工具:sed 和 awk 。grep 擅长查找,而 sed 擅长编辑,awk 则擅长格式化报告和复杂的数据处理。
一、流编辑器 sed
sed 是一个非交互式的流编辑器。它逐行读取文本,根据预设的规则对每行进行处理,并将结果输出到标准输出。默认不修改原始文件。
基本语法: sed [选项] '地址 命令' 文件名
- 地址: 指定要操作的行。可以是行号 (如
3)、范围 (如2,5) 或正则表达式 (如/pattern/)。若省略,则应用于所有行。
常用命令:
1. d (delete) - 删除行
# 删除文件的第3行
sed '3d' /etc/passwd
# 删除文件第2到第5行
sed '2,5d' /etc/passwd
# 删除所有包含 "nologin" 字符串的行
sed '/nologin/d' /etc/passwd
2. p (print) - 打印行
p 命令通常与 -n 选项配合使用。-n 会禁止 sed 的默认输出,从而只打印被 p 命令明确指定的行。
# 只打印文件的第3行
sed -n '3p' /etc/passwd
# 只打印包含 "root" 的行 (效果类似 grep)
sed -n '/root/p' /etc/passwd
3. s (substitute) - 替换
这是 sed 最强大的命令。
语法: 's/pattern/replacement/flags'
pattern: 要匹配的正则表达式。
replacement: 要替换成的字符串。&符号在replacement中代表被匹配到的整个内容。
flags:
g: 全局替换行内的所有匹配。
i: 忽略大小写。
数字 (如2): 只替换每行中第 N 次出现的匹配。
代码示例:
# 将每行第一个 "root" 替换为 "admin"
sed 's/root/admin/' /etc/passwd
# 将所有 "nologin" 替换为 "disabled"
sed 's/nologin/disabled/g' /etc/passwd
# 给所有数字加上方括号
echo "line 123 has number 456" | sed 's/[0-9][0-9]*/[&]/g'
# 使用 -i 选项直接修改文件 (危险操作,建议先备份)
sed -i.bak 's/old_text/new_text/g' /path/to/your/file
# (-i.bak 会创建一个 .bak 后缀的备份文件)
二、报告生成器 awk
awk 是一个强大的文本处理语言,它逐行读取输入,并将每行按分隔符切分成字段,极度擅长处理列式数据和生成报告。
基本语法: awk '[选项] 'pattern { action }' 文件名
核心概念:
Record: 默认情况下,每行是一个记录。
Field: 每个记录被分隔符切分成的部分。通过$1,$2,$3... 引用,$0代表整行。
FS: 输入字段的分隔符。
NR: 当前记录的行号。
NF: 当前记录的字段数量。
Pattern (模式): 决定是否对当前行执行 action。可以是正则表达式、条件表达式或特殊的 BEGIN / END。
Action (动作): 花括号 {} 中包含的一系列命令。
常用命令与功能:
1. 打印指定字段
# 打印 /etc/passwd 文件的第一列 (用户名)
awk -F: '{print $1}' /etc/passwd
# 打印 /etc/passwd 文件的第一列和最后一列
awk -F: '{print "User:", $1, "| Shell:", $NF}' /etc/passwd
-F:: 指定字段分隔符为冒号。
2. 使用模式进行过滤
# 只打印包含 "root" 的行
awk '/root/' /etc/passwd
# 只打印第三个字段(UID)小于10的行
awk -F: '$3 < 10 {print $0}' /etc/passwd
# 只打印奇数行
awk 'NR % 2 == 1' /etc/passwd
3. 使用 BEGIN 和 END 模式
BEGIN { ... }: 在处理任何行之前执行,用于初始化或打印表头。END { ... }: 在处理完所有行之后执行,用于计算汇总和打印结果。
代码示例:
# 统计 /etc/passwd 文件的用户总数
awk 'END {print "Total users:", NR}' /etc/passwd
# 打印磁盘使用情况报告,并添加表头
df -h | awk 'BEGIN {printf "%-30s %-10s %-10s\n", "Filesystem", "Size", "Used%"} NR>1 {printf "%-30s %-10s %-10s\n", $1, $2, $5}'
三、总结
本次我们深入学习了Linux文本处理三剑客中的
sed和awk。sed作为流编辑器,其核心优势在于对文本行进行删除、打印和替换操作。而awk则更像一个微型编程语言,它按字段处理数据,擅长从结构化文本中提取信息、计算、生成格式化的报告。在实际工作中,经常将grep,sed,awk通过管道组合使用,以完成复杂的文本处理任务。
练习题
背景文件 server.log 内容:
2024-08-08 10:00:15 INFO: User 'alice' logged in from 192.168.1.10.
2024-08-08 10:02:30 DEBUG: System check initiated.
2024-08-08 10:03:05 WARNING: Disk space is running low on /dev/sda1.
2024-08-08 10:05:45 INFO: User 'bob' accessed resource 'file.txt'.
2024-08-08 10:06:22 ERROR: Failed to connect to database 'prod_db'.
题目:
- sed删除: 使用
sed命令,删除server.log中所有DEBUG级别的日志行。 - sed打印: 使用
sed命令,只打印出server.log文件的第 2 到第 4 行。 - sed替换: 使用
sed命令,将server.log中所有出现的 "User" (首字母大写) 替换为 "Client"。 - awk打印字段:
server.log的日志级别是第3个字段 (以空格为分隔符)。请使用awk命令,打印出所有日志行的日期 (第1个字段) 和日志级别。 - awk模式匹配: 使用
awk命令,只打印server.log中包含ERROR信息的完整日志行。 - awk内置变量: 使用
awk命令,打印server.log中字段数超过8个的所有行。 - awk BEGIN/END: 使用
awk命令,统计server.log中INFO,WARNING,ERROR日志各自出现的次数。 - sed综合: 写一条
sed命令,将server.log中所有INFO日志行的行首添加[INFO_LOG]前缀。 - awk与sed组合: 使用管道,先用
sed过滤出server.log中所有INFO级别的日志,然后用awk提取并打印出用户名 (假设用户名总是在单引号' '中)。 - sed替换指定出现次数: 写一条
sed命令,只替换server.log中每行第二次出现的冒号:为分号;。
答案与解析
sed删除:
sed '/DEBUG:/d' server.log- 解析:
'/DEBUG:/d'使用正则表达式/DEBUG:/作为地址,匹配所有包含 "DEBUG:" 的行,并用d命令删除它们。
- 解析:
sed打印:
sed -n '2,4p' server.log- 解析:
-n禁止默认输出。2,4是行号范围地址,p命令只打印这个范围内的行。
- 解析:
sed替换:
sed 's/User/Client/g' server.log- 解析:
s/User/Client/g将所有出现的 "User" 替换为 "Client"。g标志确保全局替换。
- 解析:
awk打印字段:
awk '{print $1, $3}' server.log- 解析:
awk默认以空格或制表符为分隔符。$1是日期,$3是日志级别。{print $1, $3}打印这两个字段。
- 解析:
awk模式匹配:
awk '/ERROR/' server.log- 解析:
awk的模式部分如果没有对应的{ action },则默认动作就是{print $0}(打印整行)。/ERROR/匹配包含 "ERROR" 的行。
- 解析:
awk内置变量:
awk 'NF > 8' server.log- 解析:
NF > 8是一个条件模式,NF是当前行的字段数。只有当字段数大于8时,条件为真,执行默认的打印整行动作。
- 解析:
awk BEGIN/END:
awk '/INFO/ {info_count++} /WARNING/ {warn_count++} /ERROR/ {error_count++} END {print "INFO:", info_count, "WARNING:", warn_count, "ERROR:", error_count}' server.log- 解析:
awk可以有多个pattern { action }组合。它会遍历每一行,匹配对应的模式并执行动作 (累加计数器)。END块在最后打印所有计数器的汇总结果。
- 解析:
sed综合:
sed '/INFO/s/^/[INFO_LOG] /' server.log- 解析:
/INFO/是地址,表示只对包含 "INFO" 的行执行命令。s/^/[INFO_LOG] /是替换命令,^匹配行首,将其替换为指定的前缀。
- 解析:
awk与sed组合:
sed -n '/INFO/p' server.log | awk -F"'" '{print $2}'解析:
sed -n '/INFO/p' server.log: 首先过滤出所有INFO日志行。|: 管道将结果传给awk。awk -F"'" '{print $2}':awk使用单引号''作为字段分隔符 (-F"'"), 这样用户名就正好是第二个字段$2。
sed替换指定出现次数:
sed 's/:/;/2' server.log- 解析:
s/pattern/replacement/flags中的flags可以是一个数字。2表示只替换每行中第二次出现的匹配项。
- 解析: