目录
基本语法
awk 命令的基本语法如下,其中 pattern 表示匹配模式,actions 表示要执行的操作。语法表示当某个文本行符合 pattern 指定的匹配规则时,执行 actions 所执行的操作。pattern 和 action 都是可选的,但是两者必须保证有一个。actions 前面的大括号需要与 pattern 位于同一行中,pattern 以及 actions 要用单引号括起来,防止被 shell 解析。
awk 'pattern { actions }'
awk 命令的匹配模式非常灵活,可以是以下任意一种:
正则表达式:需要使用斜线将正则表达式包围起来。比如:
awk '/^T/ {print}' scores.txt
表示对于 scores.txt 文件中的每一行,如果以字符 T 开头,则打印出来。另外 /^(Tom|Kon)/
正则表达式表示,以 Tom 或者 Kon 开头。
字符串匹配:这里的字符串匹配可能是上面正则表达式
的一个特例,看一个 AWK 简明教程 中的例子,比如我想打印处于 FIN_WAIT
状态的连接,可以用下面命令,下面命令匹配的是第六列包含 FIN
的行。
$ awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
1 Local-Address Foreign-Address State
6 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2
9 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2
13 coolshell.cn:80 124.152.181.209:26825 FIN_WAIT1
18 coolshell.cn:80 117.136.20.85:50025 FIN_WAIT2
其中 OFS
表示的是字段分隔符,默认是空格,这里是指定为 tab。
关系表达式:例如 x>34
,判断变量 x 与 34 是否存在大于的关系
awk '$2>80 {print}' scores.txt
表示如果第二列的值大于 80 则打印出来,
模式1,模式2:行的 range 操作,用于选择从匹配模式1
行开始,到匹配模式2
的行,匹配模式1
以及模式2
的行,也是包括在内的。
awk '/^Nancy/, $2==92 {print}'
上面的代码选择从以 Nancy 开头的行开始,到以第二列的值为 92 的行,并打印这些行。
BEGIN 让用户在指定第一行文本被处理之前所发生的操作,通常可以在这里设置全局变量
,设置记录
分隔符,以及段
分隔符等。
END 让用户设置在最后一行被读取之后发生的操作,我们以下面例子说明 BEGIN 以及 END 的使用:
假设,求所有名字以 Nancy 开头的学生的平均成绩,文件如下
name score #Tom 9 #Nancy-a 12 #Jerry 90 #Nancy-b 23
命令为:
cat scores.txt | awk 'BEGIN{RS="#"} NR!=1 && /^Nancy/ {sum+=$2; count+=1} END{print sum/count}'
使用 RS 指明 record 分隔符(一般记录分隔符为换行符 \n
,即一行为一个记录),NR!=1
指明要排除第一行,action 部分声明了两个变量,分别记录总和以及个数,然后在 END 部分求平均值。
awk的工作流程,反复执行下面 4 个步骤:
- 自动从指定的数据文件中读取行文本,这里说是
行文本
,因为一般情况下,我们将一行作为一个 record,也可以用 RS (Record Split) 指定记录分隔符。这里是自动读每一个 Record。 - 自动更新 AWK 的内置系统变量的值,例如行数变量 NR,列数变量NF,行变量$0,以及各个列变量$1,$2。
- 依次执行程序中所有的匹配模式(pattern)及其操作 (action)。
- 当执行完程序中所有的模式以及操作之后,如果数据文件中仍然有未读取的数据行,则返回到第 1 步,重复执行 1 ~ 4 的操作。
指定分隔符
可以直接使用 -F 来实现,假设有下面文件,想统计第 6 列的行数,需要排序,并统计。
/-cni/ipam/v1/ip/yp-10.72.226.0-dev/10.85.240.87
/-cni/ipam/v1/ip/yp-10.72.226.0-dev/10.85.240.88
/-cni/ipam/v1/ip/yp-10.72.226.0-dev/10.85.240.89
/-cni/ipam/v1/ip/yp-10.72.226.0-dev/10.85.240.9
首先打印第六列,然后排序,然后通过 uniq -c
进行统计。
[decent@centos]$ cat ip_list | awk -F/ '{print $6}' | sort | uniq -c
253 yp-10.72.192.0-dev
506 yp-10.72.218.0-dev
253 yp-10.72.226.0-dev
变量
可以在 BEGIN 语句中,action 部分定义变量,在 awk 中定义变量非常简单,只需啊给出变量名并且赋值即可,awk 中变量分为两种类型,分别为字符串以及数字。但是在定义 awk 变量时,无需指定变量类型,awk 会根据变量所处的环境自动判断。
内置变量:
变量 | 说明 |
---|---|
$0 | 当前正在处理的 record,一般为当前行 |
$n | 当前 record 的第 n 个字段,一般为当前行的第n列 |
NF | 当前 record 的字段数,当前行的列数 |
NR | awk 已经读取的 record 数,包括当前行 |
FS | 字段分隔符 Field split |
RS | 记录分隔符 Record split |
另外,提一下 xargs 的用法,下面两个 shell 写法是等价的。
for i in $(kubectl get pods -A | grep "Evicted" | awk '{print $1}'); do kubectl delete pod ${i}; done
用 xargs 的写法如下,可见 xargs 能读取 awk 每一行的输出,并作为输入,写法上也比 for 循环简单一点。
kubectl get pods -A | grep "Evicted" | awk '{print $1}' | xargs kubectl delete pod
补充一个通过 uniq 统计和通过 awk 过滤的命令:
cat lsof_output | awk '{print $1}' | sort | uniq -c | awk '$1 > 100 {print}'
- sort: 通过 sort 将相同的行排到一起
- uniq -c: 计算相同的行的行数,输出的列中,第一行为行数,第二列为对应的行
- awk ‘$1 > 100 {print}’: 过滤第一列大于 100 的行,并打印
示例
对某一列求和
比如查询所有镜像所占磁盘空间:过滤第一行,对第四列求和,然后打印
ctr --namespace k8s.io images list | awk 'NR!=1 {sum+=$4} END{print sum}'