今天用grep “chatmsg” ./debug.log.2018-12-10 搜索,发现用户有很多堆积消息,于是想着找出消息数太多的用户。于是awk派上用场了,以前几乎不太关注这个命令,尽管知道它的强大,但是没有实际用过。
1 查找
首先是打印相关的列
[root@localhost test1]# grep “chatmsg” ./debug.log.2018-12-10 | awk ‘{print $6,$8}’
第6列和第8列正是需要的,但是需要查找的是消息数很大的,于是需要条件查找。
2 过滤
查找消息数大于500的情况
[root@localhost test1]# grep “chatmsg” ./debug.log.2018-12-10 | awk ‘$8>500 {print $6,$8}’
这里有很多相同的用户id,于是去重。
3 去重
[root@localhost test1]# grep “chatmsg” ./debug.log.2018-12-10 | awk ‘$8>500 {print $6}’ | sort $1 | uniq
uniq 是对相同的行去重
sort $1 是对第一列排序
至此领略到了awk威力,下面系统了解wak。
1 初识
awk是流式处理程序,所谓流就好比tcp网络流一般,而这里的流指文件流,可以想象文件如水一般一行行流过awk,而awk通过预定义的命令对于数据流处理。
还是通过看一个最简单的例子理解awk:
$ awk ‘{print “Hello World!”}’
在终端执行这个命令,等待用户输入,不管输入什么都是输出hello world。print "Hello World!"就是一个命令,从stdin读取数据,按照一定规则输出到stdout。
这个例子太简单,并没有说明什么问题,下面再看一个例子:
$ awk ‘{print $1}’
输入hello world 输出为hello,不论输入什么句子都是输出第一个单词。看到这里awk的面纱解开了,通过预定义的命令 print $1 对输入的数据处理,命令格式是’{ 命令}’。awk从文件中一行行读取数据,一行数据也称之为一条记录,用$1,
2...
2 ...
2...n代表一条记录中的字段,下标是从1开始,因为$0代表整条记录原样输出,字段默认是用空格分开。
如:
$ echo “I start to study awk” | awk ‘{print $1,$5}’
输出:I awk
上面的例子都是awk通过管道读取数据,再看从文件读取数据。
$ cat data2.txt
One line of test text.
Two lines of test text.
Three lines of test text.
$ awk ‘{print $1}’ data2.txt
One
Two
Three
这里仅仅显示每行第一个字段。
1.1 分隔符
前面字段之间默认的都是空格分隔符,但是可以用-F指定别的分隔符。
比如/etc/passwd文件是以:作为分隔符。
$ awk -F : ‘{print $1}’ /etc/passwd
输出所有用户
1.2 使用多个命令
$ echo “I begin to start awk” | awk ‘{$5=“go”; print $0}’
I begin to start go
用go代替awk后输出整行,命令间需要;
1.3 从文件中读取命令
前面例子中命令是直接在终端输入的,也可以将命令写在文件中。
$ cat scripts0.awk
{print $1 "'s home directory is " $6}
$ awk -F : -f ./script2.gawk /etc/passwd
文件中多条命令
$ cat scripts1.awk
{
$5=“go”
print $0
}
$ echo “I begin to start awk” | awk -f scripts1.awk
I begin to start go
1.4 处理数据前后运行脚本
$ awk ‘BEGIN {print “The scripts1.awk File Contents:”} {print $0} END {print “End of File”}’ ./scripts1.awk
The scripts1.awk File Contents:
{
$5=“go”
print $0
}
End of File
通过BEGIN 和END 命令 处理数据前后执行命令。
2 进阶
2.1 变量
2.1.1 内建变量
变量 | 描述 |
---|---|
FS | 输入字段分隔符 |
RS | 输入记录分隔符 |
OFS | 输出字段分隔符 |
ORS | 输出记录分隔符 |
ENVIRON | 当前shell环境变量 |
NR | 已处理的输入记录数 |
FILENAME | 用作awk输入数据的数据文件的文件名 |
分隔符修改
$ awk ‘BEGIN{FS=":";OFS="\t\t" } {print $1,$6}’ /etc/passwd
脚本开始执行前设置字段分割符FS=":“和输出字段分隔符OFS=”\t\t"
$ echo “hello:world” | awk ‘BEGIN{FS=":" ;OFS="|"} {print $1,$2}’
hello|world
这个例子也是将输出分隔符设置为OFS="|"
$ cat data1
name python
age 12
addr shenzhen
name go
age 15
addr beijing
name java
age 20
addr shanghai
awk ‘BEGIN{FS="\n";RS=""} {print $1,$3}’ ./data1
name python addr shenzhen
name go addr beijing
name java addr shanghai
这里将每个用户基本信息作为一个字段FS="\n",用户作为一条记录RS="",以空行为分隔符。
环境变量
$ awk ‘BEGIN{print ENVIRON[“HOME”];print ENVIRON[“PATH”]}’
/root
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/scylladb/bin:/opt/scylladb/sbin:/root/bin
2.1.2 自定义变量
$ awk ‘BEGIN{test=“hello world”;print test}’
hello world
awk ‘BEGIN{x=4; x= x * 2 + 3; print x}’
11
awk都可以用于运算。
2.2 数组
一般编程语言和脚本都有数组,awk也有,简直就是脚本语言。
格式:arr[index]=val,这里数组是关联数组,index可以是整数或者字符串。
$ awk ‘BEGIN{var[1]=1;var[2]=4;total=var[1]+var[2];print total}’
5
再看遍历数组,arr.awk文件内容如下:
BEGIN{
arr["a"]=1
arr["b"]=2
arr[2]=3
for (i in arr)
{
print "Index:",i," - Value:",arr[i]
}
}
$ awk -f arr.awk
Index: a - Value: 1
Index: b - Value: 2
Index: 2 - Value: 3
2.3 结构化命令
2.3.1 if语句
$ cat data2
20
2
3
5
34
$ awk ‘{if ($1%2) print $1,“is jishu” ; else print $1,“is oushu”}’ ./data2
20 is oushu
2 is oushu
3 is jishu
5 is jishu
34 is oushu
奇偶数的判断,如果将脚本写在文件中跟c语法一样。
2.3.2 while语句
BEGIN{
i=1
sum=0
while(i<10){
sum+=i
i++
}
print "sum: ",sum
}
这是脚本
$ awk -f sum.awk
sum: 45
这里如果没有BEGIN就不会执行,等待数据输入才会执行,这正是流式处理。
再看一个求平均数例子
这是数据文件data3
130 120 135
160 150 170
100 110 120
这是脚本average.awk
{
sum=0
i=1
while(i<4){
sum+=$i
i++
}
print "average: ",sum/3
}
$ awk -f average.awk data3
average: 128.333
average: 160
average: 110
还有do while和for语句,和c语言一模一样。
2.4 函数
格式:function name([variables]){
statements
}
脚本func.awk对data3 每行前二个字段求和
function sum(num1,num2){
return num1+num2
}
{
print "sum: ",sum($1,$2)
}
$ awk -f func.awk data3
sum: 250
sum: 310
sum: 210
看到这里发现awk对数据处理太厉害了,用别的语言还真没这个方便。
2.5 模式
2.5.1 正则表达式
$ awk ‘BEGIN{FS=":"} /mysql/{print $0}’ /etc/passwd
mysql: x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
过滤mysql用户,/mysql/模式需要放在{}左侧。
2.5.2 匹配操作符(~)
$ awk -F: ‘$1 ~ /mysql/{print 1 , 1, 1,NF}’ /etc/passwd
mysql /bin/bash
匹配第一个字段是mysql的记录
2.5.3 数学表达式
查看passwd哪些用户属于root组
$ awk -F: ‘$4 == 0{print $1}’ /etc/passwd
root
sync
shutdown
halt
operator
$4 == 0匹配条件
参考