Linux 实战:awk 文本处理全解析
awk 是一种强大的模式扫描与文本处理语言,广泛应用于 Linux/Unix 环境下对文本和数据进行结构化处理。它可以接收标准输入、一个或多个文件,甚至其他命令的输出作为数据源。除了在命令行中直接使用,awk 更常以脚本形式编写复杂逻辑。其内置支持数组、函数等特性,与 C 语言有诸多相似之处,而灵活性正是 awk 最突出的优势。
语法格式
awk [选项] '脚本' 变量=值 文件名
常用参数
-F:指定字段分隔符(可为字符串或正则表达式)-f:从脚本文件读取 awk 命令-v变量=值:赋值变量,用于将外部变量传递给 awk
脚本基本结构
awk 'BEGIN{ print "开始" } 模式{ 命令 } END{ print "结束" }' 文件名
一个 awk 脚本通常由三部分组成:BEGIN 语句块、模式匹配语句块和 END 语句块,三者均为可选项。
工作原理:
- 首先执行 BEGIN 语句块(如果存在)
- 然后从文件或标准输入逐行读取,每行执行 pattern 语句块,直至文件全部读取完毕
- 最后执行 END 语句块(如果存在)
实例展示
# 仅有 BEGIN 和 END,无 pattern 部分
echo "hello" | awk 'BEGIN{ print "welcome" } END{ print "2025-03-15" }'
# 输出:
# welcome
# 2025-03-15
# 包含 pattern 部分,默认打印当前行
echo -e "hello" | awk 'BEGIN{ print "welcome" } {print} END{ print "2025-03-15" }'
# 输出:
# welcome
# hello
# 2025-03-15
# 注意:{print} 不指定参数时默认打印整行
# 使用 print 以逗号分隔输出,实际以空格分隔
echo | awk '{ a="hello"; b="world"; c="awk"; print a,b,c; }'
# 输出:hello world awk
# print 中双引号用于字符串拼接
echo | awk '{ a="user"; b="example"; c="com"; print a"@"b"."c; }'
# 输出:user@example.com
awk 变量
内置变量
$0:当前记录(整行)$1 ~ $n:当前记录的第 N 个字段FS:输入字段分隔符(与 -F 作用相同),默认空格RS:输入记录分隔符,默认换行符NF:当前行字段数量(列数)NR:已读取的记录数(行号),从 1 开始OFS:输出字段分隔符,默认空格ORS:输出记录分隔符,默认换行符
外部变量传递
#!/bin/bash
x=200
y=300
echo | awk '{print v1*v2}' v1=$x v2=$y
# 输出:60000
运算与判断
算术运算符
+ -:加、减* / %:乘、除、求余^ **:求幂++ --:自增、自减(可作为前缀或后缀)
# 非数值在算术运算中自动转为0
awk 'BEGIN{a="x"; print a++, --a, a--, ++a}'
# 输出:0 0 0 1
赋值运算符
= += -= *= /= %= ^= **=
正则运算符
~:匹配正则表达式!~:不匹配正则表达式
逻辑运算符
||:逻辑或&&:逻辑与
关系运算符
< <= > >= != ==
其他运算符
$:字段引用空格:字符串连接符?::三目运算符in:检查数组中是否存在某键值
正则表达式
^ 行首定位
$ 行尾定位
. 匹配任意单个字符
* 匹配0个或多个前导字符
+ 匹配1个或多个前导字符
? 匹配0个或1个前导字符
[] 匹配指定字符组内的任一字符(如 [^ab])
[^] 匹配不在指定字符组内的任一字符
() 子表达式分组
| 逻辑或(二选一)
\ 转义符
~,!~ 匹配/不匹配条件
x{m} x重复m次
x{m,} x至少重复m次
x{m,n} x重复m到n次(需加 --posix 或 --re-interval 参数)
实用示例
# 以冒号分隔,打印第2列
awk -F: '{print $2}' datafile
# 以冒号分隔,打印以"Dan"开头的行的第2列
awk -F: '/^Dan/{print $2}' datafile
# 打印以C或E开头的行的第1列
awk -F: '/^[CE]/{print $1}' datafile
# 打印第1列长度为4的行(以冒号分隔)
awk -F: 'length($1)==4 {print $1}' datafile
# 匹配包含"916"的行,打印第1列
awk -F: '/916/{print $1}' datafile
# 在以Dan开头的行的第5列前添加字母"a"
awk -F: '/^Dan/{print "a"$5}' 2.txt
# 以逗号分隔打印第2列和第1列(顺序调换)
awk -F: '{print $2","$1}' datafile
# 第5列等于68900的行,打印第1列
awk -F: '$5==68900 {print $1}' 2.txt
# 第1列为"Tommy Savage"的行,打印第5列
awk -F: '$1=="Tommy Savage" {print $5}' 2.txt
# 统计当前目录下所有文件字节数总和
ls -l | awk 'BEGIN{sum=0} {sum+=$5} END{print "总字节数:", sum}'
# 以MB为单位显示总大小
ls -l | awk 'BEGIN{sum=0} {sum+=$5} END{printf "总大小: %.2f MB\n", sum/1024/1024}'
# 算术赋值测试
awk 'BEGIN{a=20; a+=10; print a}'
# 输出:30
# 正则匹配字段内容
echo | awk 'BEGIN{str="hello123world"} str~/[0-9]+/{print "包含数字"}'
# 三目运算符示例
awk 'BEGIN{a=5; print a>3?"大":"小"}'
# 输出:大
# 匹配包含"root"的整行(使用默认空格分隔)
awk '/root/{print $0}' /etc/passwd
# 指定冒号分隔,匹配第5个字段包含"root"的行
awk -F: '$5~/root/{print $0}' /etc/passwd
# 提取网卡IP地址(适配不同Linux发行版)
ip addr show eth0 | awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print $4}'
# 将文件内容全部转为大写输出
awk '{print toupper($0)}' test.txt