引例

有个 1.txt 文件, 内容如下:

1755741361|文件1|事件1
1755741374|文件1|事件2
1755741385|文件2|事件3

第一个字段是时间戳, 在查看这个文件时为了看清时间, 需要把时间戳转为字符串:

while IFS='|' read ts rest; do
    echo "$(date -d @$ts '+%Y-%m-%d %H:%M:%S')|$rest";
done < 1.txt

结果:

2025-08-21 09:56:01|文件1|事件1
2025-08-21 09:56:14|文件1|事件2
2025-08-21 09:56:25|文件2|事件3

awk 也可以实现一样的效果, 代码更简洁:

awk -F'|' '{ OFS="|"; $1 = strftime("%Y-%m-%d %H:%M:%S", $1); print }' 1.txt

awk 简介

awk 是 Linux 文本处理三剑客之一(grep, awk, sed)擅长 按行按列 处理数据。

awk 是一个 文本处理工具,也是一门轻量的脚本语言。

主要功能: 按行处理文本,按列处理数据

使用场景: 日志分析、数据转换/提取等。

基本语法:

awk 'pattern { action }' file

pattern:模式,用来匹配哪些行需要处理. 省略 pattern 时, 所有行都执行动作
action:动作,告诉 awk 对匹配的行要做什么. 省略 action 时, 默认动作是 print $0(打印整行)

常用内置变量:
$0:当前整行内容
$1, $2, …:当前行的第1列、第2列
NF当前行的列的个数 (Number of Fields, 一共多少列)
NR当前行号 (Number of Records)
FNR:当前文件的行号 (多个文件时会重置)
FS输入分隔符 (默认空格或TAB)
OFS输出分隔符 (默认空格)
RS:输入记录分隔符 (默认换行符)
ORS:输出记录分隔符 (默认换行符)

内置函数:
length($1):返回字符串长度
substr($1, 1, 5):取子串
strftime("%Y-%m-%d %H:%M:%S", $1):时间戳转日期
toupper($1):转大写
tolower($1):转小写

用法示例

简单打印(省略 pattern)

打印整行(所有列/字段):

awk '{print $0}' file.txt
# 等价: 
awk '{print}' file.txt

打印指定列/字段:

# 打印第一列和第三列( file.txt 中的行 用 空格 或 Tab 分隔出字段/列)
awk '{print $1, $3}' file.txt

使用分隔符(假设 CSV 文件以逗号分隔):

# 指定输入分隔符为 ,
awk -F, '{print $1, $2}' file.csv

# 加不加引号(单或双), 效果一样
awk -F',' '{print $1, $2}' file.csv
awk -F"," '{print $1, $2}' file.csv

# 对于特殊字符, 不是等价的, 比如 制表符, 必须用 单/双引号:
awk -F"\t" '{print $1}' file

模式匹配(pattern)

按数值过滤:

# 只输出第三列大于 100 的行
awk '$3 > 100 {print $1, $3}' file

按字符串匹配:

# 匹配第二列等于 ERROR 的行
awk '$2 == "ERROR" {print $0}' logfile

按正则匹配:

# 打印包含 failed 的所有行
awk '/failed/' logfile

# 打印含有 error 的行及其行号
awk '/error/ {print NR, $0}' logfile

BEGIN 和 END 块

# BEGIN 在处理文件前执行, END 在处理文件后执行
awk 'BEGIN {print "Start"} {print $1} END {print "Done"}' data.txt

添加第一行标题:

awk 'BEGIN {print "Name Age"} {print $1, $2}' data.txt

数值运算

求和:

# 统计第二列的总和
awk '{sum += $2} END {print "Sum:", sum}' data.txt

备注: sum 是变量.

求平均值:

awk '{sum += $2; count++} END {print "Average:", sum/count}' data.txt

其他

数据清洗:

# 把 空格分隔的数据 转成 逗号分隔的数据(csv)
awk '{OFS=","; print $1,$2,$3}' data.txt

格式化输出:

# 对齐打印
awk '{printf "%-10s %-5s\n", $1, $2}' data.txt

变量

在 awk 中, 如果变量没有被赋值, 默认值是:
数值上下文: 0
字符串上下文: “"(空字符串)

例子:

# x未定义
awk 'BEGIN {print x, x+10}'

结果:

 10

解释:
第一个 x 是空字符串 “”;
第二个 x 在算术运算中 被转换为 0, 结果 0+10=10

awk 会根据上下文自动识别 变量 为 字符串 或 数字

变量赋值

直接赋值,无需声明。

awk 'BEGIN {name="Tom"; age=20; print name, age}'

自动创建变量

awk 会在第一次引用变量时自动创建它
例子:

# 统计文件的行数: awk 每遍历一行, count 就累加 1  
awk '{count++} END {print count}' file.txt

count 第一次被用在 count++ 时自动创建,初始值为 0,然后逐行累加。

使用自定义变量

(1) 在 BEGIN 块里定义变量

# 定义 threshold=100,只打印第二列大于100的行
awk 'BEGIN {threshold=100}   $2 > threshold   {print $1, $2}' data.txt

(2) 使用-v 选项传递变量

# 定义 threshold=100,只打印第二列大于100的行(与上面 BEGIN 写法等价)
awk -v threshold=100   '$2 > threshold {print $1, $2}'   data.txt

数组

awk 可以用 关联数组(Associative Array) 或 索引数组 的方式来存储和处理数据, 而且 无需事先定义 数组的大小或类型

awk 中数组的特点:

  • 无需声明: 使用时直接赋值即可, awk 会自动创建数组。
  • 索引可以是数字(普通的索引数组)或字符串(关联数组/哈希表)。
  • 默认是关联数组 (Associative Array)。
  • 元素不存在时, 默认值是空字符串 "” 或数值 0

关联数组 示例:

awk 'BEGIN {
    fruit["b"] = "apple"
    fruit["a"] = "banana"
    fruit["c"] = "cherry"

    for (key in fruit) {
        print "Key:", key, "Value:", fruit[key]
    }
}'

备注: for 语句参考 控制语句.

结果(顺序不固定):

Key: a Value: banana
Key: b Value: cherry
Key: c Value: apple

索引数组 (普通数组) 示例:

awk 'BEGIN {
    arr[1] = "apple"
    arr[2] = "banana"
    arr[3] = "cherry"

    for (i=1; i<=3; i++) {
        print "Index:", i, "Value:", arr[i]
    }
}'

结果:

Index: 1 Value: apple
Index: 2 Value: banana
Index: 3 Value: cherry

其他示例:

# 统计第二列中每个值的出现次数
awk '{arr[$2]++} END {for (k in arr) print k, arr[k]}' file

# 统计 Nginx 日志中某个 IP 出现次数:  
awk '{ip_count[$1]++} END {for (ip in ip_count) print ip, ip_count[ip]}' access.log

控制语句

在 awk 中, 控制语句的语法和 C 语言非常相似, 支持条件判断、循环、跳转等语句。

if 语句

语法:

if (condition)
    statement
else if (condition)
    statement
else
    statement

判断字段值的大小:

awk '{if ($2 > 50) print $1, "及格"; else print $1, "不及格"}' scores.txt

while 循环

语法:

while (condition)
    statement

例子:

# 打印 1 到 5
awk 'BEGIN {
    i=1;
    while (i<=5) {
        print i;
        i++;
    }
}'

for 循环

# 传统 for 循环
awk 'BEGIN {
    for (i=1; i<=5; i++)
        print "Line", i;
}'

# for-in 循环 (遍历数组)
awk 'BEGIN {
    arr["Tom"]=78;
    arr["Jerry"]=85;
    arr["Lucy"]=90;

    for (name in arr)
        print name, arr[name];
}'

break 和 continue

break: 立即跳出当前循环
continue: 跳过当前循环的剩余语句, 进入下一次循环

awk 'BEGIN {
    for (i=1; i<=10; i++) {

        if (i == 3) continue;

        if (i > 5) break;

        print i;
    }
}'

exit 语句

exit 用于提前结束 awk 的执行, 可以带状态码。

打印文件的前三行, 然后退出:

awk '{ print $0; if (NR == 3) exit }' file.txt