行文本处理神器awk

历史

不同版本

本文的所有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

简单的例子

awk '{print $2}' top

USER
root
root
admin
root
root
operadm+
root
operadm+
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

这句命令包含两层意思

命令格式

-f代表将awk的语句写在脚本文件中。多个目标file用空格分隔

awk [options] 'script' file(s)
awk [options] -f scriptfile file(s)

常用选项

awk -F: '{print $0}' top
以:作为分隔符

可以指定多个分隔符

awk -F',| |=' '{print $0}' top
以逗号(,)、空格( )、等号(=)作为分隔符
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

awk '{print $2}' top
USER
root
root
admin
root
root
operadm+
root
operadm+
root
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 '$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

AWK编程

true和false

内建变量

变量 描述
$0 完整的输入记录(一整行)
$n 当前记录的第n个字段
ARGC 命令行参数数目
ARGV 包含命令行参数的数组
FILENAME 当前文件名
NF 当前记录的字段数
NR 当前记录数
FNR 同NR,但相对于当前文件
FS 字段分隔符(默认是任何空格)
RS 记录分隔符(默认是一个换行符)
OFS 输出字段分隔符(默认值是一个空格)
ORS 输出记录分隔符(默认值是一个换行符)

运算符

条件语句

if(expression) {

} else if(expression) {

}

循环语句

for(i=1;i<=NF;i++) {
  print $i;
}

数组

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;
}

一个例子

时间 线程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

文件命名为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

常用例子

find .m2/repository -path "*finance-api*" |gawk -F'\x2f' 'a[$7]++>=0{}END{for(i in a) print i}'
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
awk 'NR==FNR{a[$0]=1} NR>FNR{ if(!a[$0]) {print $0}}' file2 file1
awk '!a[$0]++{print $0}' test 
文章目录