"Aho-Corasick-Weinberger" 的缩写(以三位发明者命名),是一个用于文本处理和数据提取的强大命令行工具,本质是一种解释型编程语言。
该工具擅长按行处理文本,支持模式匹配、字段分割、条件判断和计算,广泛用于日志分析、数据格式化、报表生成等场景,尤其适合处理结构化或半结构化文本(如 CSV、日志文件)。
awk [选项] '模式{动作}' <输入文件1> <输入文件2> ...
awk [选项] -f 脚本文件 <输入文件1> <输入文件2> ...核心逻辑:逐行读取输入文本,对每行先判断是否匹配“模式”(如行号、包含特定字符),若匹配则执行“动作”(如打印字段、计算数值);无“模式”时默认处理所有行,无“动作”时默认打印匹配行。
内置变量:awk 有大量预设变量(如
$0代表整行、$1代表第1个字段、NF代表字段总数),是其灵活性的核心。
常用选项
常用内置变量(核心)
参考示例
以下示例均基于典型生产环境场景(如日志分析、CSV处理),使用的测试文件(access.log、user.csv)格式如下:
access.log(Nginx访问日志,空格分隔):192.168.1.1 - - [10/Mar/2024:14:30:00 +0800] "GET /index.html HTTP/1.1" 200 1234 10.0.0.5 - - [10/Mar/2024:14:31:00 +0800] "POST /login HTTP/1.1" 401 567 192.168.1.1 - - [10/Mar/2024:14:32:00 +0800] "GET /api/data HTTP/1.1" 200 890user.csv(CSV文件,逗号分隔):id,name,age,dept 1,Zhang San,28,Engineering 2,Li Si,35,Product 3,Wang Wu,42,Finance
示例1:提取日志中的IP和访问状态码(基础字段提取)
需求:从 access.log 中提取客户端IP(第1个字段)和HTTP状态码(第9个字段),并以“IP: 状态码”格式输出。
命令:
awk '{print "IP:", $1, "Status:", $9}' access.log输出:
IP: 192.168.1.1 Status: 200
IP: 10.0.0.5 Status: 401
IP: 192.168.1.1 Status: 200示例2:筛选CSV中特定部门的用户(条件匹配)
需求:从 user.csv 中筛选“Engineering”部门(第4个字段)的用户,且排除表头(第1行)。
命令:
awk -F ',' 'NR>1 && $4=="Engineering" {print $2, "(", $3, "years old)"}' user.csv解析:
-F ',':以逗号为字段分隔符;NR>1:跳过第1行(表头);$4=="Engineering":仅匹配第4个字段为“Engineering”的行;动作:打印用户名($2)和年龄($3)并格式化输出。
输出:
Zhang San ( 28 years old)示例3:统计访问日志中各IP的访问次数(计数与排序)
需求:统计 access.log 中每个客户端IP的访问次数,并按次数降序排列。
命令:
awk '{ip_count[$1]++} END{for(ip in ip_count) print ip, ip_count[ip]}' access.log | sort -k2nr解析:
ip_count[$1]++:定义数组ip_count,以IP($1)为键,每遇到相同IP则计数+1;END{...}:所有行处理完后执行的动作(遍历数组并打印IP和次数);sort -k2nr:对结果按第2列(次数)降序(-r)、数值(-n)排序。输出:
192.168.1.1 2
10.0.0.5 1示例4:计算CSV中用户的平均年龄(数值计算)
需求:从 user.csv 中计算所有用户的平均年龄(排除表头)。
命令:
awk -F ',' 'NR>1 {total_age+=$3; count++} END{print "Average Age:", total_age/count}' user.csv解析:
total_age+=$3:累加所有用户的年龄($3)到变量total_age;count++:统计用户总数;END块:计算并打印平均年龄(总年龄/用户数)。输出:
Average Age: 35示例5:替换日志中的日期格式(文本替换)
需求:将 access.log 中的日期格式从“10/Mar/2024”改为“2024-03-10”(第4个字段,含中括号)。
命令:
awk 'BEGIN{mon["Jan"]="01"; mon["Feb"]="02"; mon["Mar"]="03"} {
split(substr($4,2,11), date, "/"); # 提取日期部分(去掉开头的[)并按/分割
$4="[" date[3] "-" mon[date[2]] "-" date[1]; # 重构日期格式
print $0 # 打印修改后的整行
}' access.log解析:
BEGIN块:定义月份映射数组mon(将“Mar”转为“03”);substr($4,2,11):提取第4个字段中从第2个字符开始的11个字符(即“10/Mar/2024”);split(...):将提取的日期按“/”分割为数组date(date[1]=10,date[2]=Mar,date[3]=2024);重构第4个字段并打印整行。
输出:
192.168.1.1 - - [2024-03-10:14:30:00 +0800] "GET /index.html HTTP/1.1" 200 1234
10.0.0.5 - - [2024-03-10:14:31:00 +0800] "POST /login HTTP/1.1" 401 567示例6:批量处理多个文件并添加文件名标识(跨文件处理)
需求:处理 access_1.log 和 access_2.log 两个日志文件,提取IP和状态码,并在每行前添加文件名。
命令:
awk '{print FILENAME ": IP=" $1 ", Status=" $9}' access_1.log access_2.log解析:FILENAME 是 awk 内置变量,代表当前处理的文件名,用于区分多文件的输出。
输出(示例):
access_1.log: IP=192.168.1.1, Status=200
access_2.log: IP=172.16.0.3, Status=200注意:awk 的模式和动作需用单引号
' '包裹(避免Shell解析变量);若需在动作中使用Shell变量,可通过-v选项传递(如awk -v shell_var=$VAR '{print shell_var}'),或用双引号包裹脚本(需转义$等特殊字符)。