Shell 的 awk 命令详解(新手详细版)
本文档面向零基础新手,从“awk 是干什么的”讲起,详细说明记录与字段、模式与动作、内置变量、常用函数、格式化输出、实用示例等,并配有大量示例。
一、awk 是什么?
1.1 一句话理解
awk 是一种按行处理文本的工具(也是一门小语言):把每一行拆成若干列(字段),你可以按条件选行、按列取值、做计算、格式化输出。
适合处理表格型文本(如 CSV、空格/制表符分隔的列、日志的固定格式)。
- 按行:默认一行叫一条记录。
- 按列:默认用空格(或制表符)把一行拆成字段,第 1 列是
$1,第 2 列是$2,整行是$0。 - 模式 + 动作:满足“模式”的行才执行“动作”;不写模式则每行都执行;不写动作则默认打印整行。
1.2 典型用途
- 取某一列或几列(如日志里的时间、状态码);
- 按某列筛选(如第 3 列大于 100 的行);
- 对某列求和、求平均、计数;
- 按自定义格式输出(类似表格、报表);
- 简单“列式”解析(比纯 grep/sed 更顺手)。
二、基本语法
awk '模式 { 动作 }' [输入文件...]
- 模式:可选。用来筛选“对哪些行”做动作(如
NR==1、/error/、$3>100)。 - 动作:可选。用
{ }包起来,里面写语句(如print $1)。
若只有模式没有动作,默认动作是 print $0(打印整行);若只有动作没有模式,则对每一行执行该动作。 - 输入:不写文件则从标准输入读(管道或键盘)。
示例:
# 打印每一行的第 1 列
awk '{ print $1 }' file.txt
# 只处理第 1 列等于 "root" 的行,并打印整行
awk '$1=="root"' file.txt
# 打印每行第 1 列和第 3 列,用逗号隔开
awk '{ print $1, $3 }' file.txt
三、核心概念:记录与字段
3.1 记录(Record)
- 默认一条记录 = 一行(由换行符分隔)。
- 内置变量 RS(Record Separator)可改“什么算一条记录”,一般不用改。
3.2 字段(Field)
- 默认一行按空白(空格、Tab)拆成多个字段。
- $0:整行。
- $1、$2、$3、…:第 1、2、3、… 列。
- $NF:最后一列(NF = Number of Fields,当前行有多少列)。
- $(NF-1):倒数第二列,依此类推。
示例文件 score.txt:
zhangsan 80 90 85
lisi 70 75 72
wangwu 88 92 90
# 打印第 1 列(姓名)
awk '{ print $1 }' score.txt
# zhangsan
# lisi
# wangwu
# 打印第 1 列和最后一列
awk '{ print $1, $NF }' score.txt
# zhangsan 85
# lisi 72
# wangwu 90
# 打印整行
awk '{ print $0 }' score.txt
3.3 指定列分隔符:-F
默认用空白分列。若列之间是逗号、冒号、竖线等,用 -F 指定:
# 逗号分隔(如 CSV)
awk -F',' '{ print $1, $3 }' file.csv
# 冒号分隔(如 /etc/passwd)
awk -F':' '{ print $1, $7 }' /etc/passwd
# 多个字符当分隔符(如 " | ")
awk -F' | ' '{ print $1 }' file.txt
在程序里也可以写 FS(Field Separator):
awk 'BEGIN{FS=":"} { print $1 }' /etc/passwd
四、内置变量(常用)
| 变量 | 含义 |
|---|---|
| NR | 当前是第几条记录(行号,多文件时连续累加) |
| FNR | 当前文件中是第几行(换文件会重置) |
| NF | 当前行有多少个字段 |
| FS | 输入字段分隔符(默认空白) |
| OFS | 输出时字段之间的分隔符(默认空格) |
| RS | 输入记录分隔符(默认换行) |
| ORS | 输出记录分隔符(默认换行) |
| FILENAME | 当前正在处理的文件名 |
示例:
# 带行号打印第 1 列
awk '{ print NR, $1 }' file.txt
# 只处理列数大于 3 的行
awk 'NF>3 { print $0 }' file.txt
# 打印每行最后一列
awk '{ print $NF }' file.txt
# 输出时用逗号连接各列
awk 'BEGIN{OFS=","} { print $1, $2, $3 }' file.txt
五、模式(筛选哪些行)
5.1 不写模式
对每一行执行动作。
awk '{ print $1 }' file.txt
5.2 行号:NR、FNR
# 只处理第 1 行(表头)
awk 'NR==1 { print $0 }' file.txt
# 跳过第 1 行(从第 2 行开始)
awk 'NR>1 { print $1 }' file.txt
# 只处理第 2 到 5 行
awk 'NR>=2 && NR<=5 { print $0 }' file.txt
# 只处理奇数行
awk 'NR%2==1' file.txt
5.3 正则:/…/
# 只处理包含 "error" 的行
awk '/error/ { print $0 }' log.txt
# 只处理第 1 列匹配 "root" 的行
awk '$1~/root/' file.txt
# 不包含 "debug" 的行
awk '!/debug/' file.txt
5.4 列内容比较
# 第 1 列等于 "admin"
awk '$1=="admin"' file.txt
# 第 3 列大于 100(数值比较)
awk '$3>100' file.txt
# 第 2 列不等于 "N"
awk '$2!="N"' file.txt
# 最后一列大于 80
awk '$NF>80' score.txt
5.5 范围模式:从某行到某行
# 从含 "start" 的行到含 "end" 的行(含首尾)
awk '/start/,/end/' file.txt
5.6 BEGIN 与 END(只执行一次)
- BEGIN { 动作 }:在读入任何一行之前执行一次(常用来设 FS、OFS、打印表头)。
- END { 动作 }:处理完所有行之后执行一次(常用来打印合计、平均值)。
# 先打印表头,再打印每行第 1、3 列,最后打印 "--- END ---"
awk 'BEGIN{ print "Name Score" } { print $1, $3 } END{ print "--- END ---" }' score.txt
# 对第 2 列求和(假设全是数字)
awk '{ sum+=$2 } END{ print sum }' file.txt
六、动作:print 与 printf
6.1 print
- print $1:打印第 1 列,末尾自动换行。
- print $1, $2:打印多列,中间用 OFS(默认空格)隔开。
- print “文字”, $1:字符串和列可以混着打。
awk '{ print "姓名:", $1, "分数:", $2 }' score.txt
6.2 printf(格式化)
printf 不自动换行,需要自己加 n;格式和 C 语言类似。
常用格式符:
| 格式 | 含义 |
|---|---|
| %s | 字符串 |
| %d | 整数 |
| %f | 浮点数 |
| %n.mf | 浮点,总宽 n,小数 m 位 |
| %-10s | 左对齐、宽 10 的字符串 |
# 左对齐 10 格姓名,右对齐 5 格分数
awk '{ printf "%-10s %5dn", $1, $2 }' score.txt
# 保留两位小数
awk '{ printf "%.2fn", $2 }' file.txt
七、变量与运算
7.1 自定义变量
- 变量不用声明,直接用即可;未赋值时数值为 0,字符串为空。
- 常见用法:累加、计数、保存上一行等。
# 对第 2 列求和
awk '{ sum+=$2 } END{ print sum }' file.txt
# 统计行数(和 wc -l 类似)
awk 'END{ print NR }' file.txt
# 统计第 3 列大于 60 的行数
awk '$3>60 { count++ } END{ print count }' score.txt
7.2 简单运算
- 算术:
+、-、*、/、%。 - 比较:
==、!=、>、<、>=、<=。 - 逻辑:
&&、||、!。 - 字符串连接:用空格把字符串和变量放一起,如
print $1, $2。
# 打印第 2 列与第 3 列的和
awk '{ print $2+$3 }' score.txt
# 打印第 2 列平均值
awk '{ sum+=$2 } END{ print sum/NR }' file.txt
八、条件与循环(在动作里)
8.1 if-else
# 第 2 列大于 80 打印 "优秀",否则打印 "一般"
awk '{ if($2>80) print $1, "优秀"; else print $1, "一般" }' score.txt
8.2 for
# 打印每一行的每一列
awk '{ for(i=1;i<=NF;i++) print $i }' file.txt
# 倒序打印各列
awk '{ for(i=NF;i>=1;i--) print $i }' file.txt
8.3 while
awk '{ i=1; while(i<=NF){ print $i; i++ } }' file.txt
九、常用内置函数
9.1 字符串
| 函数 | 含义 | 示例 |
|---|---|---|
| length(s) | 字符串长度,不写 s 则对 $0 | length($1) |
| substr(s, start, len) | 子串,从 start 起取 len 个字符 | substr($1,1,3) |
| index(s, t) | t 在 s 中第一次出现的位置 | index($0,"error") |
| split(s, a, sep) | 把 s 按 sep 拆成数组 a,返回段数 | split($0,a,":"); print a[1] |
| sub(r, t, s) | 在 s 中把第一个匹配 r 的换成 t | sub(/old/,"new",$0) |
| gsub(r, t, s) | 在 s 中把所有匹配 r 的换成 t | gsub(/old/,"new",$0) |
| tolower(s) / toupper(s) | 转小写/大写 | tolower($1) |
示例:
# 只打印第 1 列长度大于 5 的行
awk 'length($1)>5' file.txt
# 打印第 1 列的前 3 个字符
awk '{ print substr($1,1,3) }' file.txt
# 把每行第 1 列转大写
awk '{ print toupper($1) }' file.txt
# 把第 2 列的 "old" 全换成 "new" 再打印
awk '{ gsub(/old/,"new",$2); print $2 }' file.txt
9.2 数学
- int(x):取整。
- rand():0~1 随机数;srand():设随机种子。
- sqrt(x)、sin(x) 等也有,需要时查手册。
awk '{ print int($2) }' file.txt
十、多文件与 FILENAME
处理多个文件时:
- NR 会一直增加;FNR 在每个文件内从 1 开始。
- FILENAME 是当前文件名。
# 每个文件的第一行前打印文件名
awk 'FNR==1 { print "---", FILENAME, "---" } { print $0 }' file1.txt file2.txt
十一、脚本写在文件里:-f
命令很长时,可以把 awk 程序写进文件,用 -f 指定:
script.awk:
BEGIN { print "开始" }
$3>60 { print $1, $3 }
END { print "结束" }
awk -f script.awk score.txt
十二、实用示例汇总
12.1 取列
# 第 1 列
awk '{ print $1 }' file.txt
# 第 1 列和最后一列
awk '{ print $1, $NF }' file.txt
# 第 2 到第 4 列(用循环)
awk '{ for(i=2;i<=4;i++) printf "%s ", $i; print "" }' file.txt
12.2 按列筛选
# 第 3 列大于 80
awk '$3>80' score.txt
# 第 1 列等于 "zhangsan"
awk '$1=="zhangsan"' file.txt
# 第 2 列在 60 到 80 之间
awk '$2>=60 && $2<=80' score.txt
12.3 求和、平均、计数
# 第 2 列求和
awk '{ sum+=$2 } END{ print sum }' file.txt
# 第 2 列平均
awk '{ sum+=$2 } END{ print sum/NR }' file.txt
# 统计行数
awk 'END{ print NR }' file.txt
# 第 3 列大于 60 的个数
awk '$3>60 { n++ } END{ print n+0 }' score.txt
12.4 格式化表格
# 表头 + 对齐列
awk 'BEGIN{ printf "%-10s %5sn", "Name", "Score" }
{ printf "%-10s %5dn", $1, $2 }' score.txt
12.5 解析 /etc/passwd
# 打印用户名和 shell(第 1 列和第 7 列)
awk -F':' '{ print $1, $7 }' /etc/passwd
# 只打印 UID>=1000 的用户名
awk -F':' '$3>=1000 { print $1 }' /etc/passwd
12.6 去重某列(简单做法)
# 按第 1 列去重,保留第一次出现(需事先按该列排序)
awk '!seen[$1]++' file.txt
12.7 管道配合
# 只取 ls -l 的第 9 列(文件名)
ls -l | awk '{ print $9 }'
# 取 ps 输出的某列
ps aux | awk '{ print $1, $11 }'
十三、与 grep、sed 的简单对比
| 需求 | 更适合的工具 |
|---|---|
| 按“行”搜关键字、取整行 | grep |
| 按“行”替换、删行、插行 | sed |
| 按“列”取、算、按列条件筛选、格式化列 | awk |
三者可组合:grep ... | awk ...、awk ... | sed ...。
十四、常见注意点
- 列从 1 开始:$0 是整行,$1 是第 1 列。
- 比较字符串用双引号:
$1=="root",不要写成$1==root(会当变量)。 - 数值比较:若某列是数字,直接
$2>80即可;若混有非数字,可能得到意外结果,可先转成数字。 - 分隔符:列不是空格分隔时,记得用 -F 或 BEGIN{FS=”…”}。
- 单引号:awk 程序一般放在单引号里,避免 shell 先解析
$1等。
十五、选项与用法速查
| 选项 | 含义 |
|---|---|
| -F’x’ | 字段分隔符为 x |
| -f 文件 | 从文件读 awk 程序 |
| -v var=值 | 定义变量,如 -v OFS=',' |
| 写法 | 含义 |
|---|---|
$0 |
整行 |
$1,$2,...,$NF |
第 1、2、…、最后一列 |
NR |
当前行号(多文件累计) |
FNR |
当前文件内行号 |
NF |
当前行列数 |
BEGIN{ } |
读行前执行一次 |
END{ } |
读行后执行一次 |
print $1 |
打印第 1 列 |
printf "%sn", $1 |
格式化打印 |
十六、小结
- awk 按行处理,每行按分隔符拆成字段;用模式选行,用动作处理(打印、计算、格式化)。
- 必会:
$0、$1、$NF、NF、NR;-F 指定分隔符;BEGIN/END;print/printf;简单比较与累加。 - 进阶:条件与循环、字符串函数、多文件、-f 脚本。
- 适合表格型文本的取列、筛选、汇总;和 grep、sed 配合可完成大部分日常文本处理。
建议用一份带多列的数据(如 score.txt、/etc/passwd)多练:取列、按列筛选、求和与平均、printf 对齐,再结合 shell的grep命令.md、shell的sed命令.md 一起用,文本处理就会很顺手。
文档以常见 awk(如 GNU awk gawk)为准;不同实现细节可能略有差异。