shell的awk命令

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. 列从 1 开始:$0 是整行,$1 是第 1 列。
  2. 比较字符串用双引号$1=="root",不要写成 $1==root(会当变量)。
  3. 数值比较:若某列是数字,直接 $2>80 即可;若混有非数字,可能得到意外结果,可先转成数字。
  4. 分隔符:列不是空格分隔时,记得用 -FBEGIN{FS=”…”}
  5. 单引号: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/ENDprint/printf;简单比较与累加。
  • 进阶:条件与循环、字符串函数、多文件、-f 脚本。
  • 适合表格型文本的取列、筛选、汇总;和 grep、sed 配合可完成大部分日常文本处理。

建议用一份带多列的数据(如 score.txt、/etc/passwd)多练:取列、按列筛选、求和与平均、printf 对齐,再结合 shell的grep命令.mdshell的sed命令.md 一起用,文本处理就会很顺手。


文档以常见 awk(如 GNU awk gawk)为准;不同实现细节可能略有差异。

发表评论