🔍搜索⌘K

Linux Shell 高效文本处理:gawk 编辑器全面指南

概述

在 Linux 里处理文本,sed 这个流编辑器擅长快速改字符、替换内容,但碰到「提取结构化数据、生成格式化报告、加复杂逻辑」这类活,就有点吃力了。

而 gawk(GNU 版的 awk)正好补上这个空缺 —— 它不单是个编辑器,更像一门轻量的编程语言,能像写程序一样去处理文本。它好在哪,主要就这几点:

  1. 按列拆文本:自动把每行按空格、冒号这些切成一列一列的,想取第几列取第几列;
  2. 能写逻辑:能定义变量、做加减乘除和字符串运算、写 if-else 判断和循环,复杂点的活也能干;
  3. 能出报表:能把乱糟糟的日志、配置文件整理成表格或者统计结果,分析日志特别好用。

一句话:sed 适合”改文字”,gawk 适合”算数据”。

一、gawk 基础:命令格式与核心选项

想上手 gawk,先得弄清楚它的命令结构和常用选项,后面写脚本都靠这个打底。

1. 核心命令格式

gawk 怎么调用?基本格式很简单,就是「选项 + 脚本 + 目标文件」:

gawk [options] 'program' file(s)
  • options:控制 gawk 怎么跑的参数(比如指定分隔符、加载脚本文件);
  • program:gawk 脚本(用单引号包裹,包含处理数据的命令);
  • file(s):待处理的文本文件(若不指定,gawk 会从标准输入 STDIN 读取数据)。

2. 常用选项解析

gawk 常用的选项主要管这几件事:设分隔符、加载脚本、定义变量。把高频的整理成下表:

选项功能描述应用场景举例
-F fs指定字段分隔符(field separator),默认是任意空白字符(空格 / 制表符)gawk -F: ‘{print $1}’ /etc/passwd
-f file从指定文件中读取 gawk 脚本(而非在命令行写脚本),适合复杂脚本gawk -f script.gawk data.txt
-v var=value在 gawk 脚本中定义变量及默认值,支持外部参数传递到脚本内部gawk -v name=“roger” ‘{print name}’
-L keyword指定兼容模式或警告级别(如 -L posix 启用 POSIX 兼容模式),多用于调试gawk -L posix ‘{print $0}’ data.txt

二、gawk 脚本入门:从命令行到文件

gawk 脚本就是写处理逻辑的地方,有两种写法:命令行直接写(适合简单逻辑)、写成脚本文件再加载(适合复杂逻辑)。

1. 命令行脚本:简单逻辑快速实现

gawk 脚本必须放在**一对花括号 {} **中,且用单引号包裹(避免 shell 解析脚本内的特殊字符)。

基础示例:打印固定文本

最简单的 gawk 脚本就一条 print 命令(跟 echo 差不多,把内容打到屏幕上):

# 脚本逻辑:打印 "Hello World!"
gawk '{print "Hello World!"}'
  • 运行后脚本会 “卡住”—— 因为未指定输入文件,gawk 会从 STDIN(键盘)读取数据;
  • 此时输入任意文本(如 test)并按 Enter,gawk 会对每一行输入执行一次脚本,输出 Hello World!;
  • 按 Ctrl+D 生成 EOF(结束符),终止 gawk 程序。

执行效果:

gawk '{print "Hello World!"}'
test  # 手动输入
Hello World!  # gawk 输出
hello  # 手动输入
Hello World!  # gawk 输出
^D  # 按 Ctrl+D 退出

避坑提醒:脚本格式错误

要是把花括号写成圆括号,或者忘了用单引号包住,会直接报错:

# 错误1:用圆括号包裹脚本
gawk '(print "Hello World!")'
# 报错:gawk: cmd. line:1: (print "Hello World!")
# gawk: cmd. line:1:  ^ syntax error

# 错误2:忘记单引号包裹脚本
gawk {print "Hello World!"}
# 报错:bash: syntax error near unexpected token `print'

2. 脚本文件:复杂逻辑模块化管理

脚本一长、逻辑一复杂,建议把脚本存成文件(后缀一般是 .gawk 或 .awk),再用 -f 加载。

示例:从文件加载脚本

  1. 先创建脚本文件 user_shell.gawk,功能是提取 /etc/passwd 中的 “用户名” 和 “登录 shell”:
# cat user_shell.gawk
BEGIN {
  # 处理数据前打印标题(表格表头)
  print "用户ID\t\t登录Shell"
  print "------------------------"
  FS = ":"  # 定义字段分隔符为冒号(替代 -F: 选项)
}
{
  # 处理每一行数据:打印第1列(用户名)和第7列(登录Shell)
  print $1 "\t\t" $7
}
END {
  # 处理完所有数据后打印结尾提示
  print "------------------------"
  print "数据处理完成"
}
  1. 用 -f 选项加载脚本,处理 /etc/passwd 文件:
gawk -f user_shell.gawk /etc/passwd

执行效果(部分输出):

用户ID         登录Shell
------------------------
root            /bin/bash
daemon          /usr/sbin/nologin
bin             /bin/sh
sys             /bin/sh
...
roger           /bin/bash
sshd            /usr/sbin/nologin
------------------------
数据处理完成

三、gawk 核心特性:数据字段处理

gawk 最好用的一个能力是自动按字段切分文本,不用自己处理分隔符,直接用变量取某一列,分析 CSV、配置文件这类结构化数据时全靠它。

1. 字段变量:精准定位数据列

gawk 会自动为每一行文本的 “字段” 分配变量,规则如下:

  • $0:代表整行文本(最常用的变量之一);
  • $1:代表第 1 个字段(列);
  • $2:代表第 2 个字段;
  • $n:代表第 n 个字段(n 为正整数)。

示例 1:提取文件的第 1 列

假设有文本文件 data.txt,内容如下:

# cat data.txt
Apple 10 5.99
Banana 20 3.99
Orange 15 4.50

默认分隔符是空格,提取每行的第 1 列(水果名称):

gawk '{print $1}' data.txt

输出:

Apple
Banana
Orange

示例 2:指定分隔符提取 /etc/passwd

/etc/passwd 用冒号 : 分隔字段,需用 -F: 指定分隔符,提取 “用户名($1)”和“家目录($6)”:

gawk -F: '{print "用户:" $1 ",家目录:" $6}' /etc/passwd | head -3

输出:

用户:root,家目录:/root
用户:daemon,家目录:/usr/sbin
用户:bin,家目录:/bin

2. 自定义字段分隔符

除了用 -F 选项,也能在脚本里用 FS 变量指定分隔符(FS 是 gawk 自带的内置变量,全称 Field Separator),两种写法效果一样。

示例:脚本内定义分隔符

用 FS=”:” 替代 -F:,效果完全一致:

# 脚本内通过 FS 定义分隔符,无需命令行选项
gawk 'BEGIN {FS=":"} {print $1, $6}' /etc/passwd | head -3

输出:

root /root
daemon /usr/sbin
bin /bin

四、gawk 脚本进阶:多命令与执行时机

实际写脚本经常要把多条命令凑一块,还得控制它们什么时候跑——处理数据前、处理中、还是处理完。

1. 多条命令的组合方式

在 gawk 脚本中,多条命令的分隔有两种方式:

  • 命令行脚本:用分号 ; 分隔命令;
  • 脚本文件:直接换行分隔(更易读,推荐)。

示例 1:命令行多命令(修改字段 + 打印整行)

把输入的第 4 个字段改成 luojia,再把整行打印出来:

# 输入:"My name is roger",输出修改后的整行
echo "My name is roger" | gawk '{$4="luojia"; print $0}'

输出:

My name is luojia

示例 2:脚本文件多命令(定义变量 + 拼接文本)

创建脚本 user_info.gawk,用变量存储固定文本,再拼接字段输出:

# cat user_info.gawk
{
  # 定义变量 text,存储固定描述文本
  text = "的家目录是"
  # 拼接变量与字段,打印完整句子
  print $1 text $6
}

用脚本处理 /etc/passwd:

gawk -F: -f user_info.gawk /etc/passwd | head -3

输出:

root的家目录是/root
daemon的家目录是/usr/sbin
bin的家目录是/bin

2. 脚本执行时机:BEGIN、处理中、END

gawk 用 BEGIN 和 END 这两个关键字控制脚本跑在什么时候,三个块按顺序是这样:

  • BEGIN 块:在读取任何数据前执行(仅执行一次),适合初始化(如打印标题、定义变量);
  • 主脚本块(无关键字):对每一行数据执行一次(核心处理逻辑);
  • END 块:在处理完所有数据后执行(仅执行一次),适合收尾(如打印统计结果、提示信息)。

完整示例:三段式脚本生成报告

以 data.txt(销售数据)为例,生成包含 “标题、数据内容、统计结果” 的完整报告:

# 1. 准备数据文件 data.txt(商品名 销量 单价)
cat data.txt
Apple 10 5.99
Banana 20 3.99
Orange 15 4.50

# 2. 编写三段式脚本 report.gawk
cat report.gawk
BEGIN {
  # 初始化:打印标题、定义分隔符
  print "====== 商品销售报告 ======"
  print "商品名\t销量\t单价\t销售额"
  print "-------------------------"
  FS = " "  # 分隔符为空格(默认也是空格,此处为演示)
  total = 0  # 定义变量,统计总销售额
}
{
  # 处理每一行:计算销售额(销量*单价),累加总销售额
  sales = $2 * $3  # 销售额 = 第2列(销量)* 第3列(单价)
  print $1 "\t" $2 "\t" $3 "\t" sales
  total += sales  # 累加总销售额
}
END {
  # 收尾:打印总销售额
  print "-------------------------"
  print "总销售额:" total " 元"
  print "========================="
}

# 3. 执行脚本
gawk -f report.gawk data.txt

执行效果(格式清晰,包含标题、数据、统计):

====== 商品销售报告 ======
商品名    销量    单价    销售额
-------------------------
Apple    10      5.99    59.9
Banana   20      3.99    79.8
Orange   15      4.50    67.5
-------------------------
总销售额:207.2 元
=========================

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

gawk 最常被拿来干的事就是分析日志——从一堆乱糟糟的日志里提取关键信息(访问 IP、请求路径、状态码),再统计出来。

案例需求

分析 Nginx 访问日志(格式如下),统计 “每个 IP 的访问次数”,并按访问次数降序排列。

Nginx 日志片段(access.log):

192.168.1.100 - - [19/Jun/2024:10:00:01 +0800] "GET /index.html HTTP/1.1" 200 1234
192.168.1.101 - - [19/Jun/2024:10:00:05 +0800] "GET /about.html HTTP/1.1" 200 5678
192.168.1.100 - - [19/Jun/2024:10:00:10 +0800] "POST /login HTTP/1.1" 302 0
192.168.1.102 - - [19/Jun/2024:10:00:15 +0800] "GET /static/css/main.css HTTP/1.1" 200 9876
192.168.1.100 - - [19/Jun/2024:10:00:20 +0800] "GET /dashboard HTTP/1.1" 200 4321

实现步骤

  1. 确定字段分隔符:日志用空格分隔,$1 即为访问 IP;
  2. 统计 IP 次数:用数组 ip_count 存储每个 IP 的访问次数(ip_count[$1]++);
  3. 排序输出:用 sort 按访问次数从多到少排(gawk 自己排序挺麻烦的,配合 shell 工具更省事)。

完整命令

# 1. 用 gawk 统计 IP 次数,输出 "IP 次数" 格式
# 2. 用 sort -k2nr 按第2列(次数)降序排列(n:数字排序,r:逆序)
gawk '{ip_count[$1]++} END {for(ip in ip_count) print ip, ip_count[ip]}' access.log | sort -k2nr

执行效果(IP 192.168.1.100 访问次数最多):

192.168.1.100 3
192.168.1.101 1
192.168.1.102 1

六、写在后面:怎么学 gawk

1. 学习路线

  1. 先打基础:搞懂命令格式(options、脚本结构)、字段变量($0、$1)、print 怎么用;
  2. 再往上走:学 BEGIN/END 块、定义变量、用数组(比如做统计)、简单的 if-else 判断;
  3. 最后实战:拿日志分析、CSV 处理、生成报告这些场景练手,把 gawk 和 shell 里的 sort、grep 配合起来用。

2. 最佳实践

  1. 脚本存成文件:逻辑一复杂就别在命令行硬敲了,存成 .gawk 文件,好改也好复用;
  2. 减少硬编码:用 -v 选项传递外部变量(如 gawk -v date=“2024-06-19” ‘{print date, $0}’ log.txt),提升脚本灵活性;
  3. 用好内置变量:除了 FS(字段分隔符),gawk 还有好几个自带变量,能少写不少重复代码,常用的有这些:
内置变量功能描述
NR已读取的总行数(处理到第几行),适合添加行号(如 print NR, $0)
NF当前行的总字段数(有几列),适合判断行格式是否正确(如 if(NF!=3) print “格式错误”)
FILENAME当前处理的文件名,多文件处理时可区分来源(如 print FILENAME, $1)
OFS输出字段分隔符(Output Field Separator),默认是空格(如 BEGIN {OFS=" "})

示例:用 NR 和 OFS 为文件添加行号并指定输出分隔符:

gawk 'BEGIN{OFS="|"} {print NR, $1, $2}' data.txt

输出(行号 + 字段用 | 分隔):

1|Apple|10
2|Banana|20
3|Orange|15
  • 跟 shell 其他工具配合:gawk 擅长提取和统计数据,但排序(sort)、去重(uniq)、过滤(grep)这些活,交给 shell 工具更顺手,效率也更高(前面日志分析那个例子就是用 sort -k2nr 排的序);
  • 调试技巧:复杂脚本先拿 -v FS=“分隔符” 试一下字段切得对不对,再一点一点往上加逻辑(比如 gawk -F: ‘{print $1, $6}’ /etc/passwd | head),别一上来写一大堆,出问题不好查。

3. 常见误区与避坑

  • 混淆字段变量与普通变量:字段变量($1、$2)代表 “列数据”,普通变量(如 total、text)代表 “自定义值”,引用普通变量时无需加 $(错误:print $total;正确:print total);
  • 忘了处理空行或格式不对的行:实际文件里常有空行、或者某行列数对不上的情况,得用 if 条件把它过滤掉(比如 if(NF==0) next 跳过空行,next 就是”扔掉这一行,看下一行”);

示例:跳过空行,只处理字段数为 3 的行:

gawk 'NF==0 {next} NF!=3 {print "警告:" NR "行字段数错误"} NF==3 {print $1, $2*$3}' data.txt
  • 分隔符设错:要是文本是用”多个空格”或者”制表符加空格”混着分隔的,不用管它——gawk 默认把”任意空白字符”都当成分隔符,不用手动去写 FS=” +“(多个空格);
  • 脚本里的引号打架:要是脚本里得写单引号(比如英文缩写),得用转义符 ’ 处理(比如 print $1 “‘s home is ” $6),不然会跟脚本外面的单引号冲突。

七、拓展:gawk 与其他工具的对比

Linux 里处理文本不只有 gawk 一个,搞清楚它跟别的工具差在哪,你才知道什么场景该用哪个:

工具核心优势劣势适用场景
gawk支持编程逻辑、字段提取、报告生成语法较复杂,简单替换效率低于 sed日志分析、CSV 处理、结构化数据统计
sed快速文本替换、行编辑,语法简洁不支持复杂逻辑,字段处理能力弱批量替换字符、删除空行、添加行首尾内容
grep高效过滤行(按关键词、正则)不支持字段提取和数据处理日志检索、筛选包含特定关键词的行
cut轻量级字段提取(按列号、分隔符)不支持复杂逻辑,仅能提取无法计算简单场景的字段提取(如 cut -d: -f1 /etc/passwd)

选择建议:

  • 仅需 “过滤行”→ 用 grep;
  • 仅需 “简单替换 / 行编辑”→ 用 sed;
  • 仅需 “轻量级字段提取”→ 用 cut;
  • 需 “字段计算、统计、生成报告”→ 用 gawk。

八、写在后面

gawk 算是 Linux 里处理文本的一把多面手,它最实在的价值是把「改改文字」升级成「用程序处理数据」。从最基础的取某一列,到分析日志做统计,再到生成格式化的报告,都能用很简短的代码搞定。

学 gawk 不用死记语法,关键是多找场景练——比如拿它分析系统日志、处理 CSV 报表、从配置文件里抠关键参数。练多了你会发现,有些原本要写几十行 Python 或 Shell 的活,gawk 几行就解决了。

看到这你应该能上手 gawk 了,平时处理文本也能省不少事。想再往下钻,推荐看 GNU 官方文档(Gawk User’s Guide),里面还有正则、自定义函数、关联数组这些进阶内容。