AWK 是一个通用脚本语言,主要设计用来对文本进行高级处理。它最常用于报告和分析工具。
和大部分程序语言不同,AWK 是数据驱动的,它意味着你可以针对输入文本进行一系列定义好的动作。它处理数据,转换数据,并且将处理结果发送到标准输出。
本文涉及 AWK 编程语言的基础知识。了解awk 这些基础知识将会很大程度上提高你在终端上操作文本的能力。
AWK
如何工作的awk 有很多不同的实现。我们将会使用 GNU 版本的 awk 实现,它被称为 gawk。在大多数 Linux 系统中,awk
就是gawk
的快捷方式。
Awk 处理文本数据,不管是从文件来的或者数据流中来的。输入数据被区分为记录和文本域。 Awk 一次处理一条记录,一直到输入结束。记录通常被称为记录分隔符的字符分隔。默认的记录分隔符是换行字符,它意味着文本中的一行数据就是一条记录。一个新的记录分隔符可以通过RS
变量进行设置。
记录由被文本域分隔符的文本域组成。默认情况下,文本域由一个空白字符构成,包括一个或者多个 tab,space 和 换行字符。
每条记录中的文本域以美元符号($)加上文本域编号来表示,以1开始。第一个文本域代表 $1, 第二个 $2,依此类推。最后一个文本域可以使用特殊变量$NF
表示。整个记录可以标识为$0
。
这是一个可见标识图,显示记录和文本域如何标识:
tmpfs 788M 1.8M 786M 1% /run/lock
/dev/sda1 234G 191G 31G 87% /
|-------| |--| |--| |--| |-| |--------|
$1 $2 $3 $4 $5 $6 ($NF) --> fields
|-----------------------------------------|
$0 --> record
想要使用awk
处理文本,你需要告诉命令如何做。这个程序包含了一系列规则和用户预定义的函数。每个规则包含一个样式和一个动作。规则由换行符或者分号(;)分隔。典型的,一个 AWK 程序类似这样:
pattern { action }
pattern { action }
...
当awk
程序处理数据时,如果样式匹配了记录,它会在这个记录上执行指定的动作。当规则没有包含任何样式,那么所有的记录(行)都被被匹配。
一个 awk 动作由一个大括号包围,并且由表达式组成。每一个表达式指定一个可以被执行的操作。一个动作可以包含一个或者多个表达式,以换行符或者分号(;)分隔。如果规则没有动作,它默认是打印所有的记录。
Awk 支持多种不同的表达式,包括条件表达式,输入输出表达式等等。最常用的 awk 表达式是:
exit
- 停止执行程序并退出next
- 停止处理当前记录并且移动到输入数据的下一条记录print
- 打印记录,文本域,变量 和自定义文本printf
- 格式化打印,类似于C 和 bash 的 printf当写 awk 程序的时候,所有在符号#后面的内容,一直到行尾,都是注释。 很长的一行数据可以使用\
符号打破成多行内容。
一个 awk 程序可以以多种方式运行。如果程序是简单的,简短的,它可以在命令行直接传给 awk 处理。
awk 'program' input-file...
在命令行运行程序的时候,它应该以单引号('')包围,以便shell 不会解释程序。
如果程序是很大的,并且很复杂的,它最好被放进文件,并且通过使用-f
选项来传递文件给awk
命令:
awk -f program-file input-file...
在下面的例子中,我们将会使用一个名为"teams.txt"的文件,它看起来像下面这样:
Bucks Milwaukee 60 22 0.732
Raptors Toronto 58 24 0.707
76ers Philadelphia 51 31 0.622
Celtics Boston 49 33 0.598
Pacers Indiana 48 34 0.585
awk 中的样式控制关联的动作是否被执行。
Awk 支持不同类型的样式,包括,正则表达式,关系表达式,范围表达式和指定的表达式样式。
如果一个规则没有样式,每一个输入记录都被匹配了。下面是一个包含一个动作的规则:
awk '{ print $3 }' teams.txt
这个程序将会打印每条记录的第三个文本域:
60
58
51
49
48
一个正则表达式匹配一系列字符串。Awk 正则表达式样式使用//
来包裹。
/regex pattern/ { action }
大部分基础例子中都是一个简单的字符匹配或者一个字符串匹配。例如,显示每条记录中包含"0.5"的第一个文本域,你可以运行下面的命令:
awk '/0.5/ { print $1 }' teams.txt
输出:
Celtics
Pacers
样式可以是任何一种其他的正则表达式。这是一个例子,打印任何以两个或者更多数字开头的记录的第一个文本域:
awk '/^[0-9][0-9]/ { print $1 }' teams.txt
输出:
76ers
关系表达式通常被用来匹配指定文本域或者变量的内容。
默认情况下,正则表达式样式匹配记录。想要对文本域进行正则匹配,指定文本域并且使用包含的操作符(~
)匹配样式。
例如,想要打印第二个文本域包含"ia"的记录的第一个文本域,你将输入:
awk '$2 ~ /ia/ { print $1 }' teams.txt
输出:
76ers
Pacers
想要匹配不包含指定样式的文本域,使用!~
操作符:
awk '$2 !~ /ia/ { print $1 }' teams.txt
输出:
Bucks
Raptors
Celtics
你可以比较字符串和数字,例如大于等于,小于等于,等于等等。下面的命令打印了第三个文本域大于等于50的所有记录中的第一个文本域。
awk '$3 > 50 { print $1 }' teams.txt
输出:
Bucks
Raptors
76ers
一个范围表达式样式包含两个样式,由逗号分隔:
pattern1, pattern2
所有以第一个样式开始,直到第二个样式被匹配的内容,这些记录都被匹配到。
下面是一个例子,它将匹配包含 “Raptors” 直到 “Celtics"的所有记录,并打印出他们的第一个文本域:
awk '/Raptors/,/Celtics/ { print $1 }' teams.txt
输出:
Raptors
76ers
Celtics
这些样式可以是关系表达式。下面的命令将会打印所有记录,从第四个文本域等于31开始到第四个文本域等于33的所有记录:
awk '$4 == 31, $4 == 33 { print $0 }' teams.txt
输出:
76ers Philadelphia 51 31 0.622
Celtics Boston 49 33 0.598
范围样式不能和其他的样式表达式一起使用。
Awk 包含下面的特殊样式:
BEGIN
- 被用来在处理记录之前 先执行的动作END
- 被用来处理记录之后 再执行的动作这个 BEGIN
样式通常被用来设置变量,END
通常用来处理数据,例如计算。
下面的例子将会打印"Start Processing.",然后打印出每条记录的第三个文本域,最后"End Processing.”
awk 'BEGIN { print "Start Processing." }; { print $3 }; END { print "End Processing." }' teams.txt
输出:
Start Processing
60
58
51
49
48
End Processing.
如果一个程序,只有一个BEGIN
样式,动作将会被执行,输入不会被处理。 如果一个程序只有END
样式,输入将被处理,然后动作被执行。
Gnu 版本的 awk 还包括两个特殊的样式 BEGINFILE
和 ENDFILE
, 这允许你在处理文件前后执行动作。
Awk 允许你将两种或者更多样式通过逻辑与(&&
)和逻辑或(||
)来进行混合使用。
这是一个使用&&
操作符的例子,用来打印第三个文本域大于等于50和第四个文本域小鱼等于30的记录中的第一个文本域:
awk '$3 > 50 && $4 < 30 { print $1 }' teams.txt
输出:
76ers Philadelphia 51 31 0.622
Celtics Boston 49 33 0.598
Awk 有一些内建变量,它们包含了很有用的信息,并且允许你在程序运行的时候调用。下面是一些最常用的内建变量:
NF
- 记录中的域序号NR
- 当前记录的序号FILENAME
- 当前处理的输入文件名称FS
- 文本域分隔符号RS
- 记录分隔符号OFS
- 输出文本域分隔符ORS
- 输出记录分隔符这是一个例子,显示如何打印文件名和行数:
awk 'END { print "File", FILENAME, "contains", NR, "lines." }' teams.txt
输出:
File teams.txt contains 5 lines.
AWK 变量可以在程序的任何一行中设置。想要在整个程序中定义一个变量,你应该在BEGIN
样式中定义这个变量。
默认的文本域分隔符是任意数量的空格或者 tab 符号。它可以通过FS
变量来修改。
例如,想要设置.
为文本域分隔符,你需要用:
awk 'BEGIN { FS = "." } { print $1 }' teams.txt
输出:
Bucks Milwaukee 60 22 0
Raptors Toronto 58 24 0
76ers Philadelphia 51 31 0
Celtics Boston 49 33 0
Pacers Indiana 48 34 0
文本域分隔符可以被设置为多个字符:
awk 'BEGIN { FS = ".." } { print $1 }' teams.txt
在命令行中运行 awk 时,你可以使用-F
选项来修改文本域分隔符:
awk -F "." '{ print $1 }' teams.txt
默认情况下,记录分隔符是一个换行符,并且可以使用RS
变量修改。
这是一个例子,演示如何将记录分隔符修改为.
:
awk 'BEGIN { RS = "." } { print $1 }' teams.txt
输出:
Bucks Milwaukee 60 22 0
732
Raptors Toronto 58 24 0
707
76ers Philadelphia 51 31 0
622
Celtics Boston 49 33 0
598
Pacers Indiana 48 34 0
585
Awk 动作被大括号({}
)包裹,并且在样式匹配时执行。一个动作可以有0个或者更多个表达式。多个表达式会按照它们的顺序依次执行,并且必须被换行符和分号分隔。
下面是一些在 awk 中支持的动作类型:
if
,for
,while
,switch
等等)print
和printf
这个print
表达式是最常用的 awk 表达式,它打印出格式化的文本,记录,文本域和变量
当打印多个条目时,你需要使用逗号分隔开。下面是一个例子:
awk '{ print $1, $3, $5 }' teams.txt
被打印的条目被一个空格符分隔开:
Bucks 60 0.732
Raptors 58 0.707
76ers 51 0.622
Celtics 49 0.598
Pacers 48 0.585
如果你不使用逗号,在打印出的条目之间将没有空白:
awk '{ print $1 $3 $5 }' teams.txt
打印的条目被拼接在一起:
Bucks600.732
Raptors580.707
76ers510.622
Celtics490.598
Pacers480.585
当print
被不带参数使用时,它默认指向print $0
。打印当前记录。
想要打印自定义文本,你需要使用双引号引用文本:
awk '{ print "The first field:", $1}' teams.txt
输出:
The first field: Bucks
The first field: Raptors
The first field: 76ers
The first field: Celtics
The first field: Pacers
你也可以打印特殊字符,例如换行符:
awk 'BEGIN { print "First line\nSecond line\nThird line" }'
输出:
First line
Second line
Third line
printf
表达式给你更多输出格式的控制。这是一个例子,插入行号:
awk '{ printf "%3d. %s\n", NR, $0 }' teams.txt
printf
不会在每个记录后面创建一个新的换行符,因此我们使用\n
:
1. Bucks Milwaukee 60 22 0.732
2. Raptors Toronto 58 24 0.707
3. 76ers Philadelphia 51 31 0.622
4. Celtics Boston 49 33 0.598
5. Pacers Indiana 48 34 0.585
下面的命令计算了每一行被存储在第三个文本域的值之和:
awk '{ sum += $3 } END { printf "%d\n", sum }' teams.txt
输出:
266
这是另外一个例子,样式使用表达式和控制表达式来打印从1到5的数字的平方:
awk 'BEGIN { i = 1; while (i < 6) { print "Square of", i, "is", i*i; ++i } }'
输出:
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25
类似上面的一行命令可能会难以理解和维护。在编写较长程序的时候,你需要创建一个独立的程序文件(prg.awk):
BEGIN {
i = 1
while (i < 6) {
print "Square of", i, "is", i*i;
++i
}
}
通过将文件名传递给awk
,运行程序:
awk -f prg.awk
你可以运行一个 awk 程序,通过使用shebang
指令和设置:
#!/usr/bin/awk -f
BEGIN {
i = 1
while (i < 6) {
print "Square of", i, "is", i*i;
++i
}
}
保存文件,并且使得它可执行:
chmod +x prg.awk
现在你可以通过输入下面的命令来执行这个程序:
./prg.awk
如果你在 shell 脚本中使用awk
命令,你可能需要传递 shell 变量给 awk 程序。一个 选项是使用双引号定义变量,并且在程序中替换这个变量。不管怎么样,选项将会使得你的程序更复杂,因此你需要避免使用 awk 变量。
推荐在 awk 程序中使用 shell 变量的方式是将 shell 变量赋值给一个 awk 变量。下面是一个例子:
num=51
awk -v n="$num" 'BEGIN {print n}'
输出:
51
Awk 是最强大的文本操作工具之一。
本文只讲了 awk 编程语言的皮毛。想要学习更多关于 awk 的知识,请参阅 Gawk 官方文档。
原文 :https://linuxize.com/post/awk-command/