行文本处理神器awk
历史
- AWK是贝尔实验室1977年提出的文本处理神器
- AWK是一种优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一
- AWK提供了极其强大的功能:可以进行正则表达式的匹配、样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数
不同版本
- 目前是主流服务器都是自带gawk,即GNU awk
- mac系统自带的是awk,建议重新安装成gawk
本文的所有awk例子的样本数据: top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7390 root 20 0 4474336 174424 14564 S 23.6 2.2 1847:10 java
7437 root 20 0 3987188 671160 12072 S 3.0 8.4 219:04.92 java
3310 admin 20 0 5050656 606644 11920 S 0.7 7.6 1:03.24 java
1114 root 20 0 0 0 0 S 0.3 0.0 616:33.63 rcuos/0
1115 root 20 0 0 0 0 S 0.3 0.0 300:38.08 rcuos/1
2732 operadm+ 20 0 4473804 361188 5232 S 0.3 4.5 128:20.90 java
1485 root 20 0 168096 11384 5700 S 0.3 0.1 76:21.38 AliYunDun
4262 operadm+ 20 0 123528 1548 1116 R 0.3 0.0 0:00.01 top
1111 root 20 0 49716 3360 1608 S 0.0 0.0 6:48.16 systemd
简单的例子
- 输出第2列
awk '{print $2}' top
USER
root
root
admin
root
root
operadm+
root
operadm+
root
- 输出第二列等于root的行
awk '$2=="root"{print $0}' top
7390 root 20 0 4474336 174424 14564 S 23.6 2.2 1847:10 java
7437 root 20 0 3987188 671160 12072 S 3.0 8.4 219:04.92 java
1114 root 20 0 0 0 0 S 0.3 0.0 616:33.63 rcuos/0
1115 root 20 0 0 0 0 S 0.3 0.0 300:38.08 rcuos/1
1485 root 20 0 168096 11384 5700 S 0.3 0.1 76:21.38 AliYunDun
1111 root 20 0 49716 3360 1608 S 0.0 0.0 6:48.16 systemd
AWK语言详解
概览
awk '$2=="root"{print $0}' top
这句命令包含两层意思
- 对top的每行按照空格(默认分隔符)进行分割
- 对top文件中每行应用模式$2==”root”,符合条件的行执行动作print $0
命令格式
-f代表将awk的语句写在脚本文件中。多个目标file用空格分隔
awk [options] 'script' file(s)
awk [options] -f scriptfile file(s)
常用选项
- -F 指定输入文件分隔符
awk -F: '{print $0}' top
以:作为分隔符
可以指定多个分隔符
awk -F',| |=' '{print $0}' top
以逗号(,)、空格( )、等号(=)作为分隔符
- -v 指定自定义参数
awk -v a=5 '{print $0,a}' top
语言格式
awk就是一个pattern-action语句的序列
pattern{action}
pattern{action}
例如在awk ‘$2==”root”{print $0}’ top中, $2==”root”是pattern,print $0是action
- 一些语句中可以没有pattern
awk '{print $2}' top
USER
root
root
admin
root
root
operadm+
root
operadm+
root
- 一些语句中可以没有action及其大括号,默认就是输出整行
awk '$2=="root"' top
7390 root 20 0 4474336 174424 14564 S 23.6 2.2 1847:10 java
7437 root 20 0 3987188 671160 12072 S 3.0 8.4 219:04.92 java
1114 root 20 0 0 0 0 S 0.3 0.0 616:33.63 rcuos/0
1115 root 20 0 0 0 0 S 0.3 0.0 300:38.08 rcuos/1
1485 root 20 0 168096 11384 5700 S 0.3 0.1 76:21.38 AliYunDun
1111 root 20 0 49716 3360 1608 S 0.0 0.0 6:48.16 systemd
- awk检查你的程序以确认不存在语法错误后,一次读取一行输入,并对每一行按序执行模式。对于每个匹配到当前输入行的模式,执行其关联的动作。不存在模式,则匹配每个输入行,因此没有模式的每个动作对于每个输入行都要执行。一个仅包含模式的模式-动作语句将打印匹配该模式的每个输入行
- 多个模式-动作语句可以以分号或者换行分隔,也可以不使用任何符号分隔。但是当不是标准的模式-动作时,必须使用符号分隔
awk '$2=="root"{print $0};$2=="admin"{print $0}' top
7390 root 20 0 4474336 174424 14564 S 23.6 2.2 1847:10 java
7437 root 20 0 3987188 671160 12072 S 3.0 8.4 219:04.92 java
3310 admin 20 0 5050656 606644 11920 S 0.7 7.6 1:03.24 java
1114 root 20 0 0 0 0 S 0.3 0.0 616:33.63 rcuos/0
1115 root 20 0 0 0 0 S 0.3 0.0 300:38.08 rcuos/1
1485 root 20 0 168096 11384 5700 S 0.3 0.1 76:21.38 AliYunDun
1111 root 20 0 49716 3360 1608 S 0.0 0.0 6:48.16 systemd
- 6种模式
- BEGIN{action}
在读取任何输入前执行一次actionawk 'BEGIN{print "hello world"};$2=="admin"{print $2}' top hello world admin
- END{action}
读取所有输入之后执行一次actionawk 'END{print "hello world"};$2=="admin"{print $2}' top admin hello world
- 表达式{action}
对于 表达式 为真(即非零或非空)的行,执行action - /正则表达式/{语句}
如果输入行包含字符串与 正则表达式 相匹配,则执行actionawk '$0~/ad/' top 3310 admin 20 0 5050656 606644 11920 S 0.7 7.6 1:03.24 java 2732 operadm+ 20 0 4473804 361188 5232 S 0.3 4.5 128:20.90 java 4262 operadm+ 20 0 123528 1548 1116 R 0.3 0.0 0:00.01 top
- 组合模式{action}
一个 组合模式 通过与(&&),或(||),非(|),以及括弧来组合多个表达式;对于组合模式为真的每个输入行,执行actionawk '$2=="admin" || $2~/operadm/' top 3310 admin 20 0 5050656 606644 11920 S 0.7 7.6 1:03.24 java 2732 operadm+ 20 0 4473804 361188 5232 S 0.3 4.5 128:20.90 java 4262 operadm+ 20 0 123528 1548 1116 R 0.3 0.0 0:00.01 top
- 模式1,模式2{action}
范围模式(range pattern)匹配从与 模式1 相匹配的行到与 模式2 相匹配的行(包含该行,多行只匹配第一行,没有则到最后一行)之间的所有行,对于这些输入行,执行actionawk '$2~/admin/,$2~/operadm/' top 3310 admin 20 0 5050656 606644 11920 S 0.7 7.6 1:03.24 java 1114 root 20 0 0 0 0 S 0.3 0.0 616:33.63 rcuos/0 1115 root 20 0 0 0 0 S 0.3 0.0 300:38.08 rcuos/1 2732 operadm+ 20 0 4473804 361188 5232 S 0.3 4.5 128:20.90 java
- BEGIN{action}
AWK编程
true和false
- awk中变量的默认值是空,即false
- 数字0是false,非0是true
- 空字符串是flae,非空字符串为true
- 数组默认值是false
内建变量
变量 | 描述 |
---|---|
$0 | 完整的输入记录(一整行) |
$n | 当前记录的第n个字段 |
ARGC | 命令行参数数目 |
ARGV | 包含命令行参数的数组 |
FILENAME | 当前文件名 |
NF | 当前记录的字段数 |
NR | 当前记录数 |
FNR | 同NR,但相对于当前文件 |
FS | 字段分隔符(默认是任何空格) |
RS | 记录分隔符(默认是一个换行符) |
OFS | 输出字段分隔符(默认值是一个空格) |
ORS | 输出记录分隔符(默认值是一个换行符) |
运算符
- 赋值运算符
- 二元运算符
- 逻辑与或
- 正则
~ 正则匹配 ~! 正则不匹配
- 字符串连接符
"" 双引号
- 数组操作符
in
条件语句
if(expression) {
} else if(expression) {
}
循环语句
for(i=1;i<=NF;i++) {
print $i;
}
数组
- awk数组下标可以是数字或者字符串
awk 'a[$2]++{}END{for(k in a) {print k,a[k]}}' top
以上命令的意思就是统计第二列重复的次数,可以把这行代码拆开写在文件中,命名为name.awk,内容如下
a[$2]++{
}
END{
for(k in a) {
print k,a[k]
}
}
命令行执行如下
awk -f name.awk top
- 二元数组或者多元数组
awk的多维数组在本质上是一维数组,更确切一点,awk在存储上并不支持多维数组。awk提供了逻辑上模拟二维数组的访问方式。例 如,array[2,4] = 1这样的访问是允许的。awk使用一个特殊的字符串SUBSEP (\034)作为分割字段,在上面的例子中,关联数组array存储的键值实际上是2\0344。
类似一维数组的成员测试,多维数组可以使用 if ( (i,j) in array)这样的语法,但是下标必须放置在圆括号中。
类似一维数组的循环访问,多维数组使用 for ( item in array )这样的语法遍历数组。与一维数组不同的是,多维数组必须使用split()函数来访问单独的下标分量。split ( item, subscr, SUBSEP)
- 注意
使用默认数字做数组下表时,第一个下标是1,例如split(str,array,”:”),获得数组array的第一个元素的操作是: array[1]
内建函数
- 字符串函数
gsub(r,s) 在整个$0中用s替代r
gsub(r,s,t) 在整个t中用s替代r
index(s,t) 返回s中字符串t的第一位置
length(s) 返回s长度
match(s,r) 测试s是否包含匹配r的字符串
split(s,a,fs) 在fs上将s分成序列a
sprint(fmt,exp) 返回经fmt格式化后的exp
sub(r,s) 用$0中最左边最长的子串代替s
substr(s,p) 返回字符串s中从p开始的后缀部分
substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部分 详细说明一下各个函数的使用方法。
strtonum(s) 将数字字符串转换成数字并返回,非数字字符串返回0
- 输出函数
print
printf()
- 时间函数
systime()
- 数学函数
函数名称 | 返回值 |
---|---|
atan2(x,y) | y,x范围内的余切 |
cos(x) | 余弦函数 |
exp(x) | 求幂 |
int(x) | 取整 |
log(x) | 自然对数 |
rand() | 随机数 |
sin(x) | 正弦 |
sqrt(x) | 平方根 |
srand(x) | x是rand()函数的种子 |
int(x) | 取整,过程没有舍入 |
rand() | 产生一个大于等于0而小于1的随机数 |
自建函数
- 定义
可以在任意位置定义。在自定义的函数中,指定的参数被当作局部变量,他们会隐藏任何同名的全局性变量。 在自定义的函数中,指定的参数被当作局部变量,他们会隐藏任何同名的全局性变量。
function add(param1, param2) {
...
return expression;
}
- awk中普通变量为值传递,awk的数组则默认为地址传递。
- 支持递归调用
一个例子
- 数据格式
时间 线程id 日志级别 类名 - 方法名
23:59:51.236 [http-bio-2080-exec-6] INFO c.c.d.api.control.Controller - test1
23:59:58.796 [http-bio-2080-exec-3] INFO c.c.d.a.c.TestController - test2
计算名字以get开始命名的方法在一段时间的qps
- awk文件
文件命名为qps.awk
BEGIN{
all_time = cal_time(startTime, endTime);
print "总时间(s)", all_time;
}
$1>=startTime && $1<=endTime{
if($6~/^get/) {
a[$6]++
}
}
END{
for(k in a) {
print k,a[k],a[k]/all_time
}
}
function cal_time(sta, end) {
split(sta,sta_a,":");
split(end,end_a,":");
h = (end_a[1] - sta_a[1]) * 60 * 60;
m = (end_a[2] - sta_a[2])* 60;
s = end_a[3] - sta_a[3];
return h + m + s;
}
- 执行语句
awk -v startTime=00:00:00 -v endTime=02:00:00 -f qps.awk log
常用例子
- 查找仓库中某个jar包的所有版本
find .m2/repository -path "*finance-api*" |gawk -F'\x2f' 'a[$7]++>=0{}END{for(i in a) print i}'
- 查询TCP连接状态
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
- 关闭进程
ps -ef | grep firefox | awk '{print $2}' | xargs kill -9
- 提取文件1中有,但文件2中没有
awk 'NR==FNR{a[$0]=1} NR>FNR{ if(!a[$0]) {print $0}}' file2 file1
- 去重
awk '!a[$0]++{print $0}' test