简介
awk
是用于基本文本处理的工具。一般遇到复杂格式等的处理问题时,应该优先想到使用awk
。比awk
更加高端的,还有perl
语言,它专门用于文本处理。python
也有丰富的文字处理功能,但是这是它的一个小部分功能,只用python处理文本有点小题大做了。一般情况下,awk
是首选。awk
特别适合那些一行有多个列、而且列之间有特定分隔号分开的情况。
和sed
类似,awk
不改变原来的文件,只是读取原来的文件的输入,然后产生新的输出。注意,awk
读取标准输入的内容作为输入;如果打开文件,那么awk
把文件内容作为标准输入。
基本语法
举个例子:
awk '{print $1}' ./log.txt
awk '{print $1 "\t" $3}' ./log.txt
第一个命令中,单引号内部的内容表示要执行的脚本,$1
表示每行的第一列。第二个命令中\t
表示输出的时候显式添加制表符,之后再输出第三列。
同时,我们可以显式的改变文本的分隔符,使用-F
命令实现。举个例子,一个文本中如果如下格式的行:
root:x:0:0:root:/root:/bin/bash
我们想要以:
为分隔符,那么执行下面的命令:
awk -F":" `{print "USER: " $1 "\tSHELL: " $7}` ./log.txt
会输出
USER: root SHELL: bash
可以设置多个分隔符甚至正则表达式来分隔文本:
awk -F"[\t ]+" '{print "DEVICE: "$1 "\tFSTYPE " $3}' /etc/fstab
[\t ]
有一个制表符和一个空格,+
表示前边的符号出现一次或者多次。在这里,说明分隔符是至少一个的制表符或者空格。
语言特性
代码结构
awk
需要有初始化的设置,这些在BEGIN
代码块中;在逻辑代码结束后,还可以有END
代码块,这些作为善后处理的工作。
实际例子:
BEGIN {
FS=":"
}
{
print "USER: " $1 "\tSHELL" $7
/nologin/ {++adder}
}
END {
print "'nologin' appears " adder "times."
}
BEGIN
代码块中,我们声明了:
作为分隔符。之后的一段是执行代码块,print
语句是输出内容,/nologin/
表示一个正则式,匹配nologin
单词,匹配上了就个adder
自增一次。END
代码块在程序结束时,输出正则匹配的次数。
注意在上述的正则式中,只有匹配上相应的正则式,后面的代码才会执行,也就是说awk
只对匹配上的文本感兴趣。
变量与数组
awk
有用户变量和自建变量,用户变量使用时不用提前声明,比如上述的adder
,可以任务awk
这个特性和shell一样。变量初始化的时候默认都是空字符串,如果涉及到数值运算,则默认为0,变量对大小写敏感。
还有内建变量,给出一个常用内建变量表:
数组命名与变量命名风格一致,也是直接使用。awk
的数则可以在方括号内以任意的数字或者字符串表达式作为索引,可以把它视为一个哈希映射,查找、插入和删除的复杂度都是。
同时,awk
数组的存储空间会自动增长,存储数据的类型是任意的,而且存储的模式是稀疏存储。如果不需要使用,直接执行delete array[index]
即可删除,delete array
是删除整个数组。变量名不能和数组重名。
例如:
site[google] = ""
site[youtube] = ""
site[baidu] = "www.baidu.com"
site[1] = 123
awk
通过ENVIRON
数组访问环境变量
举个例子:
awk 'BEGIN{print ENVITON["HOME"]; print ENVIRON["PATH"]}'
会输出HOME
环境变量。
算数运算和运算符
awk
支持+ - * / % ^
运算,^
是幂指数运算,其余同C++。给出运算符的表:
判断和循环
基本和C语言的一样。
if
条件语句:
{
if ( expression ) {
statement; statement; ...
} else if ( expression ) {
} else {
}
}
while
循环
{
while (condition) {
statement;
}
}
for
循环
{
for (x = 0; x < 4; x++) {
print "iteration", x
}
}
break
和continue
语句和C语言一致,不在赘述。
多条记录
awk
规定,对于文本文件来说,默认的一条记录就是一行。
多行记录表示把默认的一行记录改成多行。举个例子,假设有一份名单,格式如下:
Tom
Google
New York
XiaoMing
Baidu
Beijing
分别是姓名、公司和地址为连着的三行,我们需要把这三行作为一个记录来处理。那么方式为:
BEGIN {
FS="\n"
RS=""
}
{
print $1 ", " $2 ", " $3
}
FS
表示把\n
作为分隔符号,""
表示把空行作为一个记录的分隔。
输出记录分隔符
上述代码中,", "
可以进行简化替换,格式如下:
BEGIN {
FS="\n"
RS=""
OFS=", "
}
{
print $1, $2, $3
}
自定义函数
函数支持递归调用
awk
的函数支持参数输入、返回数值。函数可以定义在程序顶层的任意位置,一般格式为:
function name(arg1, arg2, ..., argn) {
statement(s)
}
指定的参数会当做局部变量,它们会隐藏同名的上一层变量。
函数分为传值和传递引用操作。awk
不支持地址操作,传递引用只能靠数组实现。return
作为返回值,如果不显式地返回,那么会返回0或者空字符串。所有函数内部未出现在参数列表中的变量,awk
会把它们视为全局变量。
awk
允许函数调用函数的参数少于函数定义中的参数,这样额外的参数会被视为局部变量。一般这类参数前面会添加额外的空白,这个额外的参数就像awk
里面其他的变量一样,在函数内容中会被初始化为空字符串。
一个重要的作用是作为存储临时数值:
function add(x, y, sum)
{
sum = x + y;
return sum;
}
{
m = 2
n = 3
x = add(m, n)
printf("sum = %d\n", sum);
}
如果涉及到引用传递,那么必须封装到数组当中进行传递:
function swap(array, temp)
{
temp = arry[1]
array[1] = array[2]
array[2] = temp
}
{
array[1] = 2
array[2] = 10
swap(array)
}
字符串与算数处理
awk
支持printf
函数,用法和C语言完全一样。其有很多字符串处理函数,给出一个内置函数表:
同时,awk
支持算数函数: