200字
文本处理三剑客-awk
2025-10-10
2025-10-16

"Aho-Corasick-Weinberger" 的缩写(以三位发明者命名),是一个用于文本处理和数据提取的强大命令行工具,本质是一种解释型编程语言。

该工具擅长按行处理文本,支持模式匹配、字段分割、条件判断和计算,广泛用于日志分析、数据格式化、报表生成等场景,尤其适合处理结构化或半结构化文本(如 CSV、日志文件)。

 awk [选项] '模式{动作}' <输入文件1> <输入文件2> ...
 awk [选项] -f 脚本文件 <输入文件1> <输入文件2> ...
  • 核心逻辑:逐行读取输入文本,对每行先判断是否匹配“模式”(如行号、包含特定字符),若匹配则执行“动作”(如打印字段、计算数值);无“模式”时默认处理所有行,无“动作”时默认打印匹配行。

  • 内置变量:awk 有大量预设变量(如 $0 代表整行、$1 代表第1个字段、NF 代表字段总数),是其灵活性的核心。

常用选项

选项

全称/说明

解释

-F

"Field Separator"

指定字段分隔符(默认以空格/制表符分割),例如 awk -F ',' 以逗号分割CSV文件。

-v

"Variable"

定义自定义变量并赋值,例如 awk -v num=10 '{print $1+num}' 定义变量 num=10 用于计算。

-f

"Script File"

从指定脚本文件中读取 awk 命令(适合复杂逻辑,避免命令行过长),例如 awk -f analysis.awk log.txt

-O

"Optimize"

优化 awk 脚本执行效率(适用于处理超大文件时提升速度),例如 awk -O '{print $3}' data.txt

-V

"Version"

显示 awk 版本信息,例如 awk -V 可查看当前系统使用的 awk 实现(如 GNU awk、mawk)。

常用内置变量(核心)

变量

说明

$0

表示当前处理的整行文本,例如 awk '{print $0}' file.txt 等价于 cat file.txt

$n

表示当前行的第 n 个字段(n 为正整数),例如 $1 是第1个字段,$NF 是最后1个字段。

NF

"Number of Fields",当前行的字段总数,例如 awk '{print NF}' file.txt 打印每行的字段数。

NR

"Number of Records",当前处理的行号(全局计数,跨文件连续),例如 awk '{print NR ":" $0}' a.txt b.txt 给两个文件的所有行编号。

FNR

"File Number of Records",当前文件内的行号(切换文件时重置为1),例如 awk '{print FNR ":" $0}' a.txt b.txt 分别给两个文件的行编号。

FS

"Field Separator",字段分隔符(等价于 -F 选项),可在脚本内修改,例如 BEGIN{FS=","} 设定逗号为分隔符。

OFS

"Output Field Separator",输出字段分隔符(默认空格),例如 BEGIN{OFS="|"} {print $1,$2} 用竖线分隔输出字段。

参考示例

以下示例均基于典型生产环境场景(如日志分析、CSV处理),使用的测试文件(access.loguser.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 890
  • user.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(...):将提取的日期按“/”分割为数组 datedate[1]=10date[2]=Mardate[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.logaccess_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}'),或用双引号包裹脚本(需转义 $ 等特殊字符)。


评论