轻量的结构化文本处理工具-awk
与其说awk是一个强大的文本处理工具,我更加喜欢称之为轻量级的C语言版脚本,有了它,你就能非常自由,轻松的操作文本文件了。ps:比Excel更加方便哦,一句话:awk可以带你装b带你飞~
初体验:九九乘法表
1 | #! /bin/awk -f |
在先看awk
程序之前,咱先来看一下由他实现的一个九九乘法表
yans-MacBook-Pro:Downloads yanyl$ seq 9 | awk -f chengfa.awk
lets begins
1x1=1
2x1=2 2x2=4
3x1=3 3x2=6 3x3=9
4x1=4 4x2=8 4x3=12 4x4=16
5x1=5 5x2=10 5x3=15 5x4=20 5x5=25
6x1=6 6x2=12 6x3=18 6x4=24 6x5=30 6x6=36
7x1=7 7x2=14 7x3=21 7x4=28 7x5=35 7x6=42 7x7=49
8x1=8 8x2=16 8x3=24 8x4=32 8x5=40 8x6=48 8x7=56 8x8=64
9x1=9 9x2=18 9x3=27 9x4=36 9x5=45 9x6=54 9x7=63 9x8=72 9x9=81
ends
yans-MacBook-Pro:Downloads yanyl$
是不是着实惊艳了一把,程序暂时不解释,不错,为了更好更加方便得处理Linux
下的文本,文本就记录学习awk
神器的基本用法^_^
awk基础结构
awk
基础结构如下1
awk 'BEGIN{commands} {commands} END{commands}' filename
这个命令敲下去之后,
awk
回去按行读取filename
的文本进行操作,里面的程序就是你对应的文本处理逻辑,还有这里的文件输入也可以改为标准的stdin
的形式哦~
很清晰,awk
可以分为3块:
BEGIN
:可以理解为程序的初始化,可以自定一些变量,或者定义文件的分隔符等- {}模式块:这里会从文件或者输入里面按行读取,在这里可以使用
内建变量
或者列的值$i
或者自定义变量等,然后使用控制结果或者内置函数来进行相应的文本处理逻辑 END
:程序执行结束块,可以做一些Summary
类型或者其他的输出
其实模式快可以省略,默认是执行
{print}
打印行内容,其实其他两块也可以省略-_-
所以总结来说awk
的结构其实可以直接看做一个循环体1
2
3
4BEGINE #进行初始化
while
{commands} #内部循环的处理逻辑
END #结尾
需要注意的是:
awk
语法的是包含在引号或者双引号里面的- 程序复杂的话可以写成
awk
结尾的文件,然后使用awk -f xxx.awk filename
来进行执行
当然了,刚刚都说了
awk
的优势在于轻量级,下面更多的demo
都是行内的语法,并不会专门去写成*.awk
文件再来执行^_^
列的操作
列的输出
演示之前先看使用的实验文本文件成绩表score.log
yans-MacBook-Pro:Downloads yanyl$ cat score.log
class name course score
301 xiaoming math 95
301 xiaoming chinese 58
301 xiaoming english 68
301 xiaohong math 77
301 xiaohong chinese 88
301 xiaohong english 75
302 peter math 35
302 peter chinese 45
302 peter english 95
这里的列于列之间是使用
tab
隔开的
先来看一下最简单的输出,假设我只关心年龄和成绩
yans-MacBook-Pro:Downloads yanyl$ awk '{print $2,$4}' score.log
name score
xiaoming 95
xiaoming 58
xiaoming 68
xiaohong 77
xiaohong 88
xiaohong 75
peter 35
peter 45
peter 95
这里的取列是使用
$i~n
从1开始计数,比如$1
是第一列,$4
是第4列,其中$0
表示整好
但是发现没对齐,我想将其对个齐:
yans-MacBook-Pro:Downloads yanyl$ awk '{printf("%-8s\t%-8s\n",$2,$4)}' score.log
name score
xiaoming 95
xiaoming 58
xiaoming 68
xiaohong 77
xiaohong 88
xiaohong 75
peter 35
peter 45
peter 95
就是就是使用了标准了
C
语言风格的printf
函数了
列的过滤
我感觉里面比较强大的就是使用awk
进行各种过滤操作了,简直了
现在我只是想查看小明的成绩
yans-MacBook-Pro:Downloads yanyl$ awk 'NR==1 || $2=="xiaoming"' score.log
class name course score
301 xiaoming math 95
301 xiaoming chinese 58
301 xiaoming english 68
这里支持and(
&&
)和or(||
)的操作,同时NR
表示当前的行号,NR==1
就是为了保留表头
同时awk
还支持C
语言系的各种比较符号!=, >, <, >=, <=
查看不及格的小兔崽子-_-
yans-MacBook-Pro:Downloads yanyl$ awk 'NR==1 || $4<60' score.log
class name course score
301 xiaoming chinese 58
302 peter math 35
302 peter chinese 45
这里除了支持直接判断的语法,还支持字符串的匹配,使用~
符号(这个有点强大),使用语法为~/pattern/
现在来看一下姓小的同学
yans-MacBook-Pro:Downloads yanyl$ awk 'NR==1 || $2~/xiao/' score.log
class name course score
301 xiaoming math 95
301 xiaoming chinese 58
301 xiaoming english 68
301 xiaohong math 77
301 xiaohong chinese 88
301 xiaohong english 75
还可以进行模式非匹配,要!~
即可
yans-MacBook-Pro:Downloads yanyl$ awk 'NR==1 || $2!~/xiao/' score.log
class name course score
302 peter math 35
302 peter chinese 45
302 peter english 95
变量
变量定义使用
awk
里面变量的几个点:
- 里面的变量命名和
C
系一样 - 变量区分大小写
- 变量一般都是数字和字符串类型
- 在初始化时,数字类型初始化为0,字符串类型初始化为空,并且在运行期间可变,同时不需要向
C
一样显示的初始化变量
这里来看一个最简单的变量的创建与读写
yans-MacBook-Pro-2:awk_test yanyl$ awk '{name="zhangsan"} END{print name}' score.log
zhangsan
yans-MacBook-Pro-2:awk_test yanyl$ awk '{age=23} END{print age}' score.log
23
这里想想
python
的变量就可以了哦~
另外参数不一定要在awk
语句中初始化,还可以在输入文件中进行传递,语法格式为awk 'statment' var=value file
的方式
yans-MacBook-Pro-2:awk_test yanyl$ awk '$4>ms' ms=60 score.log
class name course score
301 xiaoming math 95
301 xiaoming english 68
301 xiaohong math 77
301 xiaohong chinese 88
301 xiaohong english 75
302 peter english 95
yans-MacBook-Pro-2:awk_test yanyl$ awk '$4>ms' ms=90 score.log
class name course score
301 xiaoming math 95
302 peter english 95
这里示例是用于展示在输入文件的时候传一个ms
变量,来控制过滤最小分数,应该还是蛮方便的
类型转换
这里的类型转换只有数字类型和字符串类型的转换啦-_-
记住一个原则+号表示数字类型的操作,会忽略字符串,而空才是正常字符串的拼接
yans-MacBook-Pro-2:awk_test yanyl$ awk 'BEGIN{two=2;three=3;print two+three}'
5
yans-MacBook-Pro-2:awk_test yanyl$ awk 'BEGIN{two=2;three=3;print two three}'
23
yans-MacBook-Pro-2:awk_test yanyl$ awk 'BEGIN{two=2;three=3;print two+"s"+three}'
5
yans-MacBook-Pro-2:awk_test yanyl$ awk 'BEGIN{two=2;three=3;print two "s" three}'
2s3
还有关于详细的转换参考这里
内建变量
下面贴出的都是一些内置变量,在自己创建变量的时候请勿将变量名创建为下面的内置变量名
变量名 | 变量描述 |
---|---|
$0 |
表示当前正行的内容 |
$1~$n |
表示第几个列的值,这些列是使用FS 变量进行分割的 |
FS |
列的分割符,默认是空格或者tab |
NF |
表示当前文件中列的个数 |
NR |
表示当前读取的行数 |
FNR |
这个表示当前文件自己标的行号 |
NR |
表示当前读取的行数 |
RS |
表示输入记录的换行符,默认为换行符\n |
OFS |
表示输出记录的分割符,默认是空格 |
ORS |
表示输出记录的分割符,默认是换行符 |
FILENAME |
当前输出的文件名 |
ARGC |
传入参数的个数 |
ARGV |
传入参数的数组 |
上面是列出了一些常用的内置变量,详细的可以看这里
个人感觉最常用的就是
$i
犀利以及FS
、NR
之类的了^_^
控制流程
awk
提供了类C
系语言的强大的控制流程语法,可以发现绝大部门语法和C
是一样的
if判断
if
语法是你超级熟悉的if (condition) then-body [else else-body]
现在需要给每个人每门成绩打一个标签,85分以上优秀,60及以上良好,否则。。。
1 | #! /bin/awk -f |
将这个awk
文件进行执行之后可以看到
yans-MacBook-Pro-2:awk_test yanyl$ awk -f test.awk score.log
class name course score label
301 xiaoming math 95 excellent
301 xiaoming chinese 58 shit
301 xiaoming english 68 well
301 xiaohong math 77 well
301 xiaohong chinese 88 excellent
301 xiaohong english 75 well
302 peter math 35 shit
302 peter chinese 45 shit
302 peter english 95 excellent
这里注意,你如果想把控制语句写到一行,就必须得使用{}
显示标注执行主体,;
来标注结束,就是像下面这么任性我也是没办法
yans-MacBook-Pro-2:awk_test yanyl$ awk '{if (NR==1){print $0 "\tlabel";}else{if($4 >= 85){label = "excellent";}else if ($4 >= 60){label = "well";}else{label = "shit";}print $0 "\t" label;}}' score.log
class name course score label
301 xiaoming math 95 excellent
301 xiaoming chinese 58 shit
301 xiaoming english 68 well
301 xiaohong math 77 well
301 xiaohong chinese 88 excellent
301 xiaohong english 75 well
302 peter math 35 shit
302 peter chinese 45 shit
302 peter english 95 excellent
while循环
1 | while (condition) |
while
还是很方便使用
yans-MacBook-Pro-2:awk_test yanyl$ awk 'BEGIN{i=1;sum=0;while(i<10) {sum+=i;i++;} print sum }'
45
从1累加到9的实现
do-while循环
1 | do |
for循环
1 | for (initialization; condition; increment) |
这个方法在最上面的初体验里面就有了
switch
1 | switch (expression) { |
这个在
gnu awk
的手册中说明支持,但是自己捣鼓了半天也没结果,下次深入了再贴出来
next控制
既然有了上面那么强大的C
系控制,肯定也少不了break
和continue
,在awk
中都是支持的,而且用法都一样,所以这里不再一一描述,有意思的是来说一下这个next
关键字
next:在文章最上面描述的awk的结构的中间部分是一个天然的循环,next关键词就是让你在这个天然的循环中跳转到下一行处理,就像continue
1 | #! /bin/awk -f |
上面程序是用来过掉不及格的同学的
yans-MacBook-Pro-2:awk_test yanyl$ awk -f test.awk score.log
class name course score
301 xiaoming math 95
301 xiaoming english 68
301 xiaohong math 77
301 xiaohong chinese 88
301 xiaohong english 75
302 peter english 95
nextfile控制
另外一个与next
类似的就是nextfile
,表示跳到下一个输入文件(因为awk
一下子可以有多个输入文件呀)
1 | #! /bin/awk -f |
可以看到结果
yans-MacBook-Pro-2:awk_test yanyl$ awk -f test.awk n=58 score.log n=88 score.log
58 301 xiaoming math 95
58 301 xiaoming chinese 58
58 301 xiaoming english 68
58 301 xiaohong math 77
58 301 xiaohong chinese 88
58 301 xiaohong english 75
88 301 xiaoming math 95
都是符合预期的
同时还有一个exit
关键词不再介绍,结束程序用的
数组
创建与使用
awk
为了更好更加强大的处理出具,他也提供了数组这个功能,但是略有特点:
- 数组的索引不一定是要连续数据,而且还可以字符串与汉字混合(据官网doc解释,
awk
将数字型的索引都转成了字符串) - 使用数组签不需要显示声明他的长度
- 很多产生数组的函数 产生完了之后都是从1开始的(注意这个,不然得坑爹了)
来看一个比较全面的使用数组的列子1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#! /bin/awk -f
#file = test.awk
BEGIN{
#可以直接给数组赋值了,不需要定义
arr["color"]="red";
arr["age"]=23
arr[99]=999
}
{
#使用for each的方式取值,当然也可以for的三段式取值
for(x in arr)
{
print x ":" arr[x];
}
}
可以看到输出结果中将数组的索引和值都打印出来了
yans-MacBook-Pro-2:awk_test yanyl$ seq 1 | awk -f test.awk
age:23
color:red
99:999
但是需要注意的是你不能这么写if(arr["name"] == "xiaoming")
,也许awk
不会报错,但是也不会有正确结果出来,所以你可以些么写 if("name" in arr)
先进行一个是否有索引的判断,这样比较安全
其他的技巧自己看详情的doc拉
我感觉
awk
的里面的数组俨然是一个LinkedHashMap
-_-
删除
删除的语法也很简单delete array[index-expression]
,
比如1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#! /bin/awk -f
#file = test.awk
BEGIN{
arr["color"]="red";
}
{
if("color" in arr)
print "yes"
else
print "no"
delete arr["color"]
if("color" in arr)
print "yes"
else
print "no"
}
进行一个打印之后可以发现
yans-MacBook-Pro-2:awk_test yanyl$ seq 1 | awk -f test.awk
yes
no
在第二次判断数组是否存在该元素的时 可以发现已经删除掉了
如果想删除全部元素就可以直接使用这个语法:delete array
,但是要注意的是 删除了之后之后这个变量还是数组类型,此时无法被用于赋值字符串或者数字:1
2
3
4
5
6
7
8
9
10#! /bin/awk -f
#file = test.awk
BEGIN{
arr["color"]="red";
}
{
delete arr
arr=4
}
这种方式在进行第二次赋值的时候会发错
yans-MacBook-Pro-2:awk_test yanyl$ seq 1 | awk -f test.awk
awk: can't assign to arr; it's an array name.
input record number 1, file
source line number 9
关于其余的多维数组还是去看文档把~
内置函数
awk
的另一个强大之处就是提供了很丰富的内置函数,写起来非常方便!
算术函数
函数名称 | 介绍 |
---|---|
atan2(y,x) |
反正切函数 |
cos(x) |
余弦函数 |
sin(x) |
正弦函数 |
exp(x) |
e 的幂次方,任意数的幂次方可以使用x**y 来算 |
log(x) |
自然数的对数 |
sqrt(x) |
平方根 |
int(x) |
整数的值 |
rand() |
任意随机数,大于0小于1,注意得加srand() 方法设置种子 |
这里仅演示rand()
随机数1
2
3
4
5
6
7
8
9#! /bin/awk -f
#file = test.awk
BEGIN{
srand();
}
{
print rand();
}
执行得到
yans-MacBook-Pro-2:awk_test yanyl$ seq 9 | awk -f test.awk
0.107174
0.117314
0.89198
0.700518
0.269547
0.503197
0.402781
0.958053
0.523041
注意,如果不加
srand()
方法,则多次执行还是原来的随机数…
字符串函数
函数名称 | 介绍 |
---|---|
asort(source [, dest [, how ] ]) |
对数组的值进行排序 |
asorti(source [, dest [, how ] ]) |
对数组的索引进行排序 |
gensub(regexp, replacement, how [, target]) |
正则替换,how 为替换的个数,为g or G 的时候替换全部,还可以将匹配到的部分使用/N 的格式来取 |
gsub(regexp, replacement [, target]) |
正则替换 |
index(in, find) |
查找指定字符串的索引位置,不存在时返回0, 还是注意它不支持正则 |
length([string]) |
查找字符串的长度,如果查找变量未指定的时候,返回0(当做查一个数组) |
match(string, regexp [, array]) |
正则查找字符串 返回找到的位置,如果传了array 参数,查找的字符串将会丢入这个数组 |
split(string, array [, fieldsep [, seps ] ]) |
将字符串分割到数组 |
strtonum(str) |
将字符串转为数字 |
sub(regexp, replacement [, target]) |
替换指定字符串 |
substr(string, start [, length ]) |
截取指定字符串 |
sub(regexp, replacement [, target]) |
替换指定字符串 |
tolower(string) |
转小写 |
toupper(string) |
转大写 |
演示几个常用的1
2
3
4
5
6
7
8
9
10
11
12
13
14#! /bin/awk -f
#file = test.awk
BEGIN{
str="abc-def"
print substr(str,3,2) #截取字符串
sub(/cd/,"CD",str) #转大写
print str
split(str,arr,"-") #分割字符串
for(i in arr)
print arr[i]
print index(str,"c-d")
}
可以看到结果
yans-MacBook-Pro-2:awk_test yanyl$ seq 1 | awk -f test.awk
c-
abc-def
def
abc
3
自定义函数
awk
很强大的一点就是支持自定义函数
自定义函数的语法如下
function name([parameter-list])
{
body-of-function
}
还是非常简单的熟悉啊
由于
awk
是读取整个代码之后再运行的,所以自定义函数也不需要放在调用者之前,可以放任何地方,如有也返回值也直接使用return
即可
1 | #! /bin/awk -f |
调用可以有
yans-MacBook-Pro-2:awk_test yanyl$ seq 1 | awk -f test.awk
hello,tom
函数里面想使用局部变量就必须将其放到传参中
总结
awk
里面只有数值
、字符串
、数组
三种变量awk
的数组其实索引从1
开始!!!awk
的大部分语法和C一样,但是不需要预定义变量awk
非常轻量级,处理结构化的文本文件非常的方便awk
还有其他很多功能,参见官方文档,因为主要是用其轻量,所以这里不再一一介绍
参考
本作品采用[知识共享署名-非商业性使用-相同方式共享 2.5]中国大陆许可协议进行许可,我的博客欢迎复制共享,但在同时,希望保留我的署名权