awk 入门的 hello world 如下:

首先创建一个 a.txt 文件,里面的内容如下:

1
2
3
4
5
6
7
8
9
10
$ cat a.txt
Proto Recv-Q Send-Q Local-Address Foreign-Address State
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
$ awk '{ print "hello world"}' a.txt
hello world
hello world
hello world
hello world

awk 是输入驱动的,当调用 awk 程序的时候,它将读取所提供的脚本,然后 awk 将对每个输入行执行脚本中的命令。

awk 程序是由所谓的主输入循环组成的。一个循环是一个例程,它将一直重复执行直到有一些存在的条件终止它。它可以从文件中读取一行并且使得进程可以访问它。

awk 允许你编写两个特殊的例程,它们是在任何输入被读取前和所有输入都读取后执行。分别是与 BEGIN 和 END 相关的过程。并且它们两个是可选的。

可以把 awk 看成三个部分组成:处理输入前将做的处理,处理输入过程中做的处理,处理输入完成后做的处理。

看下面例子理解下三个部分:

1
2
3
4
5
6
7
8
9
$ awk 'BEGIN {print "begin"} {print $0} END {print "END"}' a.txt
begin
Proto Recv-Q Send-Q Local-Address Foreign-Address State
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 coolshell.cn:80 123.169.124.111:49809 ESTABLISHED
tcp 0 0 coolshell.cn:80 117.136.20.85:50025 FIN_WAIT2
tcp 0 0 :::22 :::* LISTEN
END

模式匹配

在 awk 读入一行时,它试图匹配脚本中的每一个模式匹配规则。只有与一个特定的模式相匹配的输入行才能成为操作对象。

如下示例:

1
2
3
4
5
$ awk '/LISTEN/ {print $0}' a.txt
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 :::22 :::* LISTEN

字段的引用和分离

awk 允许使用字段操作符 $ 来指定字段。$1 表示第一个字段,以此类推。$0 表示整行输入记录。

1
2
3
4
5
6
$ awk '{print $1,$2}' a.txt
Proto Recv-Q
tcp 0
tcp 0
tcp 0
tcp 0

可以使用 -F 选项来改变字段分隔符。并且 print 本身会输出当前行。

1
2
3
4
$ echo "a##b##c" | awk -F '##' '{print $1,$2}'
a b
$ echo "a##b##c" | awk -F '##' '{print }'
a##b##c"

在脚本中也可以指定域分隔符,可以通过系统变量 FS 来改变。因为整个必须在读取第一个输入行之前执行,所以必须在由 BEGIN 当中指定。

1
2
$ echo "a##b##c" | awk 'BEGIN { FS="##"} {print $1,$3}'
a c

我们也可以测试匹配指定的字段,使用 ~ 操作符可以测试一个字段的正则表达式。

1
2
3
4
5
6
7
8
$ awk '$6 ~ /WAIT/ {print}' a.txt
tcp 0 0 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
tcp 0 0 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2
tcp 0 0 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2
tcp 0 0 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT
tcp 0 1 coolshell.cn:80 124.152.181.209:26825 FIN_WAIT1
tcp 0 0 coolshell.cn:80 183.60.212.163:51082 TIME_WAIT
tcp 0 0 coolshell.cn:80 117.136.20.85:50025 FIN_WAIT2

字段的划分

FS 设置成一个空格,这种情况记录的前导空白字符和结尾空白字符会被忽略,并且字段用空格或制表来分隔,FS 默认的就是一个空格。

FS 设置成一个单个字符,在这个字符出现的任何地方都将分隔出另外一个字段,如果两个连续的分隔符,在它们之间的字段值为空串。

FS 设置成多个字符,它将被作为一个正则表达式来解释。也就是,字段分隔符是与正则表达式匹配的“最左边最长的非空部重叠”的字串。

表达式

一个表达式通过计算返回一个值。表达式由数字和字符串常量、变量、操作符、函数和正则表达式组成。

常量有两种类型:

  • 字符串型:”rcx”,字符串在表达式中必须用括号括起来。
  • 数字型:1

变量名只能由字母、数字和下划线组成。而且不能以数字开头。

如下表达式:

1
2
3
x=1
z="hello"
p="hello" "world" 空格是字符串连接符,将 helloworld 赋值给 p

如下例子:

1
2
3
4
5
6
$ cat a.txt
rcx 90
qwe 23
sdf 34
$ awk '{sum+=$2} END {print sum}' a.txt
147

这里面虽然 sum 没赋初始值,但在开始它的值是 0。

系统变量

awk 当中有许多系统变量或内置变量。

awk 有两种类型的系统变量:

  • 变量的默认值可以改变,默认的字段和记录分隔符
  • 变量的值可用于报告或数据处理中,例如当前行字段的数量等

看下表:

变量 解释
$0 当前记录(这个变量中存放着整个行的内容)
$1~$n 当前记录的第n个字段,字段间由FS分隔
FS 输入字段分隔符 默认是空格或Tab
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。
FNR 当前记录数,与NR不同的是,这个值会是各个文件自己的行号
RS 输入的记录分隔符, 默认为换行符
OFS 输出字段分隔符, 默认也是空格
ORS 输出的记录分隔符,默认为换行符
FILENAME 当前输入文件的名字

【参考资料】

  1. sed与awk

—EOF—