Linux awk 命令:文本处理的瑞士军刀

awk 是 Linux 下最强大的文本处理工具之一,名字取自三位创始人 Aho、Weinberger、Kernighan 的姓氏首字母。很多人只用它做简单的列提取,其实 awk 的能力远不止于此。

awk 的核心模型

awk 的工作流程可以概括为:

复制代码
awk 'pattern { action }' file
  • pattern:匹配条件(正则、表达式、范围)
  • action:执行的操作(打印、计算、变量赋值)

对于每一行,awk 会:

  1. 自动按分隔符分割字段(默认空格)
  2. 字段存入 $1, $2, $3...,整行是 $0
  3. 检查 pattern,匹配则执行 action
bash 复制代码
# 提取第一列和第三列
awk '{ print $1, $3 }' data.txt

# 只处理包含 "error" 的行
awk '/error/ { print $0 }' app.log

# 计算文件总行数
awk 'END { print NR }' data.txt

NR 是内置变量,表示当前行号(Number of Records)。END 是特殊模式,在所有行处理完后执行。

字段分隔符:不只是空格

-F 参数指定字段分隔符:

bash 复制代码
# CSV 文件按逗号分割
awk -F',' '{ print $1, $3 }' data.csv

# 使用正则表达式:一个或多个空格
awk -F'[ ]+' '{ print $1 }' data.txt

# 多字符分隔符
awk -F'|' '{ print $1 }' data.txt

也可以在脚本内设置 FS(Field Separator):

bash 复制代码
awk 'BEGIN { FS = "," } { print $1, $3 }' data.csv

BEGIN 在处理任何行之前执行,常用于初始化变量。

内置变量的秘密

awk 提供了多个内置变量:

变量 含义
$0 整行内容
$1~$n 第 n 个字段
NF 当前行字段数(Number of Fields)
NR 当前行号(全局)
FNR 当前行号(当前文件)
FS 字段分隔符
OFS 输出字段分隔符
RS 行分隔符
ORS 输出行分隔符

NF 的妙用:引用最后一个字段

bash 复制代码
# 打印每行的最后一个字段
awk '{ print $NF }' data.txt

# 打印倒数第二个字段
awk '{ print $(NF-1) }' data.txt

条件判断与循环

awk 支持 if-elsefor/while 循环:

bash 复制代码
# 按条件过滤并标记
awk '{
  if ($3 > 100) {
    print $1, "HIGH"
  } else {
    print $1, "NORMAL"
  }
}' data.txt

# 计算每行的字段和
awk '{
  sum = 0
  for (i = 1; i <= NF; i++) {
    sum += $i
  }
  print sum
}' numbers.txt

数组与统计

awk 的数组是关联数组(associative array),键可以是任意字符串:

bash 复制代码
# 统计每个单词出现次数
awk '{
  for (i = 1; i <= NF; i++) {
    count[$i]++
  }
}
END {
  for (word in count) {
    print word, count[word]
  }
}' text.txt

# 按访问量统计 HTTP 状态码
awk '{ count[$9]++ } END { for (code in count) print code, count[code] }' access.log

这段代码中 $9 是 Nginx 日志的状态码字段(假设标准格式)。

实战案例:分析 Nginx 访问日志

假设日志格式:

复制代码
192.168.1.1 - - [10/May/2026:10:30:45 +0800] "GET /api/users HTTP/1.1" 200 1234 "-" "Mozilla/5.0"

1. 统计 Top 10 访问 IP

bash 复制代码
awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head -10

纯 awk 实现:

bash 复制代码
awk '{
  ip[$1]++
}
END {
  for (i in ip) print ip[i], i
}' access.log | sort -rn | head -10

2. 计算平均响应时间

假设日志格式包含响应时间(最后一个字段):

bash 复制代码
awk '{
  total += $NF
  count++
}
END {
  print "Average:", total/count, "ms"
}' access.log

3. 提取 4xx 和 5xx 错误

bash 复制代码
# 提取所有 4xx 和 5xx 状态码的请求
awk '$9 ~ /^[45][0-9][0-9]$/ { print $0 }' access.log

# 统计错误类型分布
awk '$9 ~ /^[45][0-9][0-9]$/ {
  errors[$9]++
}
END {
  for (code in errors) print code, errors[code]
}' access.log

~ 是正则匹配操作符,$9 ~ /^.../ 表示第 9 个字段匹配正则。

性能优化技巧

1. 跳过无效行

next 跳过不需要处理的行:

bash 复制代码
awk '/^#/ { next } { print $1 }' config.conf

跳过注释行(以 # 开头)。

2. 只处理前 N 行

bash 复制代码
awk 'NR > 100 { exit } { print $1 }' data.txt

处理前 100 行后退出,避免读取整个大文件。

3. 多文件处理时的 FNR

当处理多个文件时,NR 是全局行号,FNR 是当前文件行号:

bash 复制代码
# 每个文件单独统计
awk 'FNR == 1 { print "File:", FILENAME } { print NR, FNR, $0 }' file1.txt file2.txt

复杂案例:计算移动平均

假设有一个温度数据文件,每行一个温度值,计算 3 点移动平均:

bash 复制代码
awk '{
  values[NR] = $1
  if (NR >= 3) {
    sum = values[NR] + values[NR-1] + values[NR-2]
    print (NR-2), sum/3
  }
}' temperature.txt

awk vs sed vs grep

很多人分不清这三个工具的边界:

工具 核心能力 典型场景
grep 行过滤 快速搜索匹配行
sed 流编辑 替换、删除、插入
awk 字段处理 + 计算 统计、报表、格式化

三者常组合使用:

bash 复制代码
# 组合示例:提取 error 行,替换时间戳格式,统计按小时分布
grep "ERROR" app.log | \
  sed 's/\[.*\]//' | \
  awk '{ count[$1]++ } END { for (h in count) print h, count[h] }'

小结

awk 的强大在于:

  1. 自动字段分割,省去手动 split
  2. 完整的编程语言(变量、数组、函数、循环)
  3. 内置的模式匹配机制

掌握 awk,处理文本文件就像用 SQL 查询数据库一样高效。复杂的统计、格式化、转换任务,一行 awk 命令就能搞定。


相关工具:Linux sed 命令 | 文本去重工具 | Grep 命令详解

相关推荐
江湖有缘2 小时前
从零开始:基于Docker Compose的Kener监控面板部署全记录
运维·docker·容器
躺不平的理查德2 小时前
Shell逻辑判断备忘录
运维·服务器·git
月光技术杂谈2 小时前
国内环境下安装 docker-ce 的完整步骤
运维·docker·容器
用户2367829801682 小时前
Linux df 命令深度解析:从磁盘空间监控到 inode 耗尽排查
linux
Leida_wanglin2 小时前
工作经验-问题总结
运维
其实防守也摸鱼2 小时前
软件安全与漏洞--软件安全设计
运维·网络·安全·网络安全·密码学·需求分析·软件安全
3301_3 小时前
Debian13 ThinkPad T490安装指纹解锁
linux
Liangwei Lin3 小时前
LeetCode 76. 最小覆盖子串
运维·服务器
kyriewen3 小时前
我开发的 Chrome 扒图浏览器插件又更新了❗
前端·chrome·浏览器