!!! awk入门
? awk ?    2017-04-11 21:00:02    150    0    0
simon88   ? awk ?


 

awk是什么

awk其名称来自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。 实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。 它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。

简单使用

首先介绍几个简单的实例,以便大家有个直观的了解。 有如下的基金历史数据,存储在文件000962.txt内,共有20行数据,每行有4个字段。 这里列出了前9行的数据。

1    2015-12-25    1.2366    1.2366
2    2015-12-24    1.2296    1.2296
3    2015-12-23    1.2324    1.2324
4    2015-12-22    1.2454    1.2454
5    2015-12-21    1.2351    1.2351
6    2015-12-18    1.2218    1.2218
7    2015-12-17    1.2247    1.2247
8    2015-12-16    1.1965    1.1965
9    2015-12-15    1.1906    1.1906 

如果我们想取得第3列数据大于1.2的结果,可以如下处理

$ awk '$3>1.2' 000962.txt

1    2015-12-25    1.2366    1.2366
2    2015-12-24    1.2296    1.2296
3    2015-12-23    1.2324    1.2324
4    2015-12-22    1.2454    1.2454
5    2015-12-21    1.2351    1.2351
6    2015-12-18    1.2218    1.2218
7    2015-12-17    1.2247    1.2247
15    2015-12-07    1.2080    1.2080
17    2015-12-03    1.2028    1.2028

那我们如果只需要返回日期和基金净值呢:

$ awk '$3>1.2{print $2,$3}' 000962.txt

2015-12-25 1.2366
2015-12-24 1.2296
2015-12-23 1.2324
2015-12-22 1.2454
2015-12-21 1.2351
2015-12-18 1.2218
2015-12-17 1.2247
2015-12-07 1.2080
2015-12-03 1.2028

如果我们需要在每行前加上行号的话,可以这样:

$ awk 'BEGIN{NM=0;} $3>1.2{NM+=1;print NM,$2,$3}' 000962.txt

1 2015-12-25 1.2366
2 2015-12-24 1.2296
3 2015-12-23 1.2324
4 2015-12-22 1.2454
5 2015-12-21 1.2351
6 2015-12-18 1.2218
7 2015-12-17 1.2247
8 2015-12-07 1.2080
9 2015-12-03 1.2028

记录和字段

从上面几个例子里,有涉及到记录和字段的概念,如果你已经很熟悉了,此段可以略过。

一条记录可以粗略的理解为一行。字段是一行里彼此分开的数据段,每一段就是一个字段值。 比如如下数据:

AAA 111
BBB 222
CCC 333
DDD 444

我们称为有4条记录,每条记录有3个字段。 这里有一个规则,就是我们默认为记录与记录之间是通过换行符分割的,字段与字段之间是通过空格分割的。

所以, 只要改变其中的一个规则,我们可以把上述数据表示为一下格式:

AAA+111
BBB+222
CCC+333
DDD+444

这里采用了默认的记录分隔符 --- 换行符,而没有采用默认的字段分隔符 --- 空格,而是使用了加号(+),规则是我们自己定义的。

所以对于上面提到的,『一条记录可以粗略的理解为一行』,这里的粗略就是指默认的记录分隔符---换行符。 当记录分隔符不是换行符的情况时,一行数据可能代表的就是多条记录,这取决于我们对于数据的理解。

AAA+111|BBB+222|CCC+333|DDD+444

这个也可以表示成上述数据的第三种格式。 如果我们需要把第三种数据输出成第一种,也是可以做到的。

$ echo 'AAA+111|BBB+222|CCC+333|DDD+444'|awk 'BEGIN{FS=+";RS="|"}{print $1, $2}'

AAA 111
BBB 222
CCC 333
DDD 444

这里我们使用到了AWK支持的两个参数FS和RS。FS是field separator的缩写,称为字段分隔符。 RS是recod separator的缩写,称为记录分隔符。

所以对于数据的解读,取决于我们对于记录分隔符和字段分隔符的定义。 只需要把规则告诉AWK,它就能给你想要的。

在AWK的处理过程中,我们可以通过 $0 来表示当前记录,用$1代表第一个字段,一直到$n,n就是当前记录的字段总数。

awk处理流程

awk处理的大致流程,就是以记录分隔符将文本切分成记录, 然后再对每条记录以字段分隔符为准,切分成字段,再把这些值转换成变量给到程序处理。

经过AWK的处理处理之后, $0 代表的就是整个单条的记录, $1~$n 代表的就是当前行的字段, $n 表示当前行的最后一个字段值。 AWK重复的执行代码,输入就是每行,一直到最后一条记录。

$0 的值是变动的,指示的是当前的行,在当前行上你是没有办法直接拿到上一条数据的,除非你自己保存了上行数据; 同样你也是没有办法拿到下一条数据的,你能做的就是处理完这条记录或跳过这条,等待下一条的来临。

除了按记录处理流程,awk还定义了BEGIN和END块。 BEGIN块在处理所有记录之前执行,END块在处理完所有记录后执行。

$ awk 'BEGIN{print "beggin...";NM=0;SUM=0;}{NM+=1;SUM+=$3;print;} END{print "average is:",SUM/NM}' 000962.txt

beggin...
1    2015-12-25    1.2366    1.2366
2    2015-12-24    1.2296    1.2296
... ... ... ...
19    2015-12-01    1.1838    1.1838
20    2015-11-30    1.1784    1.1784
average is: 1.20086

为了避免输出结果占用过多的篇幅,中间部分用省略号做了替代。

第二份数据,tcp连接报告。

tcp4       0      0  192.168.1.103.50660    17.250.120.76.443      FIN_WAIT_1
tcp4       0      0  192.168.1.103.50655    74.125.23.138.443      SYN_SENT
tcp4       0      0  192.168.1.103.50654    74.125.23.139.443      SYN_SENT
tcp4       0      0  192.168.1.103.50653    74.125.23.100.443      SYN_SENT
tcp4       0      0  192.168.1.103.50652    74.125.23.100.443      SYN_SENT
tcp4       0      0  192.168.1.103.50646    202.108.249.252.80     CLOSE_WAIT
tcp4       0      0  192.168.1.103.50645    202.108.249.252.80     CLOSE_WAIT
tcp4       0      0  192.168.1.103.50644    202.108.249.252.80     CLOSE_WAIT
tcp4       0      0  192.168.1.103.50643    202.108.249.252.80     CLOSE_WAIT
tcp4       0      0  192.168.1.103.50642    111.202.60.47.80       LAST_ACK
tcp4       0      0  192.168.1.103.50547    198.41.215.67.80       LAST_ACK
tcp4       0      0  192.168.1.103.49779    209.20.75.76.80        CLOSE_WAIT%

字段的处理

获取所有的本地打开的端口以及端口的连接状态。

$ cat ./tcp.txt|awk '{print substr($4,15), $6;}'
50660 FIN_WAIT_1
50655 SYN_SENT
50654 SYN_SENT
50653 SYN_SENT
50652 SYN_SENT
50646 CLOSE_WAIT
50645 CLOSE_WAIT
50644 CLOSE_WAIT
50643 CLOSE_WAIT
50642 LAST_ACK
50547 LAST_ACK
49779 CLOSE_WAIT

表达式和语句

AWK的程序由两部分组成,分为模式匹配和动作表达式,即 pattern {action} 组成。 省略 pattern 表示匹配所有行, {action} 是可以省略的,表示打印整个行。但是不能同时都省略。

AWK会对匹配表达式结果为真的行,执行对应的动作表达式。

pattern-action之间通过换行或分号进行分割。

action 表示的是一个语句序列,一个语句可以由以下部分组成:

[ ] 表示可选

  • 判断语句: if(表达式) 语句1 [ else 语句2 ]
  • 循环语句: while(表达式) 语句
  • for: for(表达式; 表达式; 表达式) 语句
  • for-in: for(变量 in 数组变量) 语句
  • do-while: do 语句 while(表达式)
  • 中断: break 只能用在 for、for-in、while、do-while里
  • 继续: continue 只能用在 for、for-in、while、do-while里
  • 语句: { [语句] } 注意这里有左右大括号
  • 表达式: 如 age = 33
  • 打印语句: print [ 表达式列表 ] [ > 表达式 ]
  • 格式化: printf 格式串 [ , 表达式类别 ] [ 表达式 ]
  • 返回值: return [表达式]
  • next: next 忽略当前行后续匹配
  • nextfile: nextfile 跳过当前文件剩余行,打开下一个文件,并从头开始
  • 删除数组元素: delete 数组名[表达式] 删除数组元素
  • 删除数组: delete 数组名
  • exit: exit [表达式]

语句之间可以通过换行符、分号、括号进行分割。

获取列表中的tcp状态,并且去掉CLOSE_WAIT,就可以使用以上的delete语句。

cat ./tcp.txt|awk '{all[$6]++}{delete all["CLOSE_WAIT"]} END{for(st in all){print st, all[st];}}'
FIN_WAIT_1 1
SYN_SENT 4
LAST_ACK 2

模式匹配

AWK是否执行一个动作,取决于输入记录是否和Pattern匹配。常见的Pattern有:

  • /regexp/ 正则表达式, 当输入记录匹配正则表达式时,执行相应动作
  • 表达式 当表达式的值不为0或不为空(作为字符串)时, 匹配结果为真,执行相应动作
  • 模式1, 模式2 这是一对Pattern, 它匹配一个范围, 表示从匹配模式1开始直到匹配2所有的记录
  • BEGINEND 这是两个特殊的Pattern, 分表表示文件的第一行被读之前和最后一行执行之后。
  • Pattern为空时,匹配所有的记录
# 匹配第一个字段包含2和3的所有的记录
$ cat ./000962.txt|awk '$1 ~ /[23]+/'
2    2015-12-24    1.2296    1.2296
3    2015-12-23    1.2324    1.2324
12    2015-12-10    1.1685    1.1685
13    2015-12-09    1.1719    1.1719
20    2015-11-30    1.1784    1.1784
# 匹配第一个字段不包含2和3的所有记录
$ cat ./000962.txt|awk '$1 !~ /[23]+/'
1    2015-12-25    1.2366    1.2366
4    2015-12-22    1.2454    1.2454
5    2015-12-21    1.2351    1.2351
6    2015-12-18    1.2218    1.2218
7    2015-12-17    1.2247    1.2247
8    2015-12-16    1.1965    1.1965
... ... 省略
18    2015-12-02    1.1778    1.1778
19    2015-12-01    1.1838    1.1838
# 打印以第11到14条的记录
cat ./000962.txt|awk '/^11/,/14/'
11    2015-12-11    1.1599    1.1599
12    2015-12-10    1.1685    1.1685
13    2015-12-09    1.1719    1.1719
14    2015-12-08    1.1774    1.1774

内置常量

  • CONVFMT
  • FS 字段分隔符,值是一个正则表达式
  • NF 当前记录的字段总个数
  • NR 当前记录的顺序号
  • FNR 当前记录位于当前文件的顺序号,处理多个文件的时候有用
  • FILENAME 当前文件名
  • RS 记录分隔符,默认为换行符
  • OFS 指定以怎样的分隔符为输出数据的字段分隔符,默认为空格
  • ORS 输出记录分隔符,默认为换行
  • OFMT 输出顺序号的格式,默认为 0.6g
  • SUBSEP
  • ARGC
  • ARGV
  • ENVIRON

内置函数

  • length(s) 把参数值当做字符串所占有的长度,如果没有指定参数默认为当前记录的总长度
  • srand 设置随机谁的种子
  • rand 返回0~1之间的一个随机数
  • int 转换字符串成数值类型
  • gsub(r,s) 在整个$0中用s替代r
  • gsub(r,s,t) 在整个t中用s替代r
  • index(s,t) 返回s中字符串t的第一位置
  • 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的后缀部分
  • system(cmd)
  • tolower(str)
  • toupper(str)
  • 如何执行AWK

  在UNIX的命令行上输入下列格式的指令:("$"表示Shell命令行上的提示符号)    

    $  awk  'awk程序'   数据文件名

  则awk会先编译该程序,然后执行该程序来处理所指定的数据文件。(上述方式直接把程序写在UNIX的命令行上)

  • awk程序的主要结构:

  awk程序中主要语法是  Pattern { Actions },故常见的awk程序其形式如下:

      Pattern1 { Actions1 }

      Pattern2 { Actions2 }

      ......

      Pattern3 { Actions3 } 

  • Pattern 是什么 ?

  awk 可接受许多不同形式的 Pattern。一般常使用 "关系表达式"(Relational expression)来当作 Pattern。

  例如:

    x > 34 是一个Pattern,判断变量 x 与 34 是否存在大于的关系。

    x == y 是一个Pattern,判断变量 x 与变量 y 是否存在等于的关系。

    上式中 x >34 、 x == y 便是典型的Pattern。

  

  awk 提供 C 语言中常见的关系运算符(Relational Operators) 如 >, <, >=, <=, ==, !=。此外,awk 还提供 ~ (match) 及 !~(not match) 二个关系运算符(注一)

  其用法与涵义如下:

    若 A 为一字符串,B 为一正则表达式(Regular Expression)

      A ~ B 判断 字符串A 中是否 包含 能匹配(match)B表达式的子字符串。

      A !~ B 判断 字符串A 中是否 不包含 能匹配(match)B表达式的子字符串。

  例如 :

    "banana" ~ /an/   整个是一个Pattern。

  因为"banana"中含有可以匹配 /an/ 的子字符串,故此关系式成立(true),整个Pattern的值也是true。

  相关细节请参考 附录 A Patterns, 附录 E Regular Expression  

(注一:) 有少数awk文献,把 ~, !~ 当成另一类的 Operator,并不视为一种 Relational Operator。本手册中将这两个运算符当成一种 Relational Operator。 

  • Actions 是什么?

  Actions 是由许多awk指令构成。而awk的指令与 C 语言中的指令十分类似。

  例如:

    awk的 I/O指令:print, printf( ), getline, ...

    awk的 流程控制指令:if(...){..} else{..}, while(...){...}, ...

    (请参考 附录 B --- "Actions" ) 

  • awk 如何处理 Pattern { Actions } ?

  awk 会先判断(Evaluate) 该 Pattern 的值,若 Pattern 判断后的值为true (或不为0的数字,或不是空的字符串),则awk将执行该 Pattern 所对应的 Actions。反之,若 Pattern 的值不为 true,则awk将不执行该 Pattern所对应的 Actions。 

  例如:若awk程序中有下列两指令

    50 > 23        {print "Hello! The word!!" }

    "banana" ~ /123/  {print "Good morning !" }

  awk会先判断 50 >23 是否成立。因为该式成立,所以awk将打印出"Hello! The word!!"。而另一 Pattern 为"banana"~/123/,因为"banana" 内未含有任何子字符串可 match /123/,该 Pattern 的值为false,故awk将不会打印出 "Good morning !" 

上一篇: 快速排序(数组和迭代器法)

下一篇: awk内置变量和字段变量

150 人读过
0 条评论