awk 命令基础

目录

基本语法

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 个步骤:

  1. 自动从指定的数据文件中读取行文本,这里说是行文本,因为一般情况下,我们将一行作为一个 record,也可以用 RS (Record Split) 指定记录分隔符。这里是自动读每一个 Record。
  2. 自动更新 AWK 的内置系统变量的值,例如行数变量 NR,列数变量NF,行变量$0,以及各个列变量$1,$2。
  3. 依次执行程序中所有的匹配模式(pattern)及其操作 (action)。
  4. 当执行完程序中所有的模式以及操作之后,如果数据文件中仍然有未读取的数据行,则返回到第 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}'