awk 入门介绍

一 序

   为了提高线上排查问题的能力,对于日志的查找分析是有要求的。所以作为Linux的常用命令,awk 少不了。

所以本篇也接着这个机会,整理下awk的入门知识。

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

二  使用方法

     AWK 是一种用于处理文本的编程语言工具。AWK 在很多方面类似于 shell 编程语言,尽管 AWK 具有完全属于其本身的语法。一般的UNIX作业系统,本身即附有AWK,不同的UNIX作业系统所附的AWK其版本亦不尽相同.

    在最初创造 AWK 时,其目的是用于文本处理,并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。该实用工具扫描文件中的每一行,查找与命令行中所给定内容相匹配的模式。如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行。尽管操作可能会很复杂,但命令的语法始终是:

awk '{pattern + action}' {filenames}

其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。 pattern就是要表示的正则表达式,用斜杠括起来。

三 调用方式

   awk提供了适应多种需要的不同解决方案,它们是:

1、 awk命令行,你可以象使用普通UNIX命令一样使用awk,在命令行中你也可以使用awk程序设计语言,虽然awk支持多行的录入,但是录入长长的命令行并保证其正确无误却是一件令人头疼的事,因此,这种方法一般只用于解决简单的问题。当然,你也可以在shell script程序中引用awk命令行甚至awk程序脚本。

2、使用-f选项调用awk程序。awk允许将一段awk程序写入一个文本文件,然后在awk命令行中用-f选项调用并执行这段程序。具体的方法我们将在后面的awk语法中讲到。

3、利用命令解释器调用awk程序:利用UNIX支持的命令解释器功能,我们可以将一段awk程序写入文本文件,然后在它的第一行加上:

#!/bin/awk -f

并赋予这个文本文件以执行的权限。这样做之后,你就可以在命令行中用类似于下面这样的方式调用并执行这段awk程序了。

awk脚本文本名 待处理文件

我的一点习惯,通常简单命令用来于指定规则浏览和抽取信息,awk抽取信息后,才能进行其他文本操作。对于编写程序还是不太习惯。主要是复杂的文本处理,还是习惯用java.

四  语法

  既然AWK是一种文本处理语言,就有自己的语法。

awk [选项参数] 'script' var=value file(s)
或
awk [选项参数] -f scriptfile var=value file(s)

选项参数说明:

  • -F fs or --field-separator fs
    指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。
  • -v var=value or --asign var=value
    赋值一个用户定义变量。
  • -f scripfile or --file scriptfile
    从脚本文件中读取awk命令。
  • -mf nnn and -mr nnn
    对nnn值设置内在限制,-mf选项限制分配给nnn的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。
  • -W compact or --compat, -W traditional or --traditional
    在兼容模式下运行awk。所以gawk的行为和标准的awk完全一样,所有的awk扩展都被忽略。
  • -W copyleft or --copyleft, -W copyright or --copyright
    打印简短的版权信息。
  • -W help or --help, -W usage or --usage
    打印全部awk选项和每个选项的简短说明。
  • -W lint or --lint
    打印不能向传统unix平台移植的结构的警告。
  • -W lint-old or --lint-old
    打印关于不能向传统unix平台移植的结构的警告。
  • -W posix
    打开兼容模式。但有以下限制,不识别:/x、函数关键字、func、换码序列以及当fs是一个空格时,将新行作为一个域分隔符;操作符**和**=不能代替^和^=;fflush无效。
  • -W re-interval or --re-inerval
    允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。
  • -W source program-text or --source program-text
    使用program-text作为源代码,可与-f命令混用。
  • -W version or --version
    打印bug报告信息的版本。

基本用法举例:有个test.txt,格式是 i am XXX,my phone is YYYYY,

要求只打印出姓名 XXXX 跟电话 YYYYY.

$ more test.txt 
i am bohu,my phone is 12345678
i am zhangsan,my phone is 6666666
$ awk -F '[ ,]' '{print $3,$7}' test.txt 
bohu 12345678
zhangsan 6666666

这里使用多个分隔符.先使用空格分割,然后对分割结果再使用","分割。 再打印出数组的指定列。

五 运算符

运算符 描述
= += -= *= /= %= ^= **= 赋值
?: C条件表达式
|| 逻辑或
&& 逻辑与
~ ~! 匹配正则表达式和不匹配正则表达式
< <= > >= != == 关系运算符
空格 连接
+ - 加,减
* / % 乘,除与求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ -- 增加或减少,作为前缀或后缀
$ 字段引用
in 数组成员

分别过滤第一列大于2的行,过滤第一列大于2并且第二列等于'Are'的行 

$ more awk.txt 
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
$ awk '$1>2' awk.txt 
3 Are you like awk
This's a test
10 There are orange,apple,mongo
$  awk '$1>2 && $2=="Are" {print $1,$2,$3}' awk.txt 
3 Are you

六 内置变量

变量 描述
$n 当前记录的第n个字段,字段间由FS分隔
$0 完整的输入记录
ARGC 命令行参数的数目
ARGIND 命令行中当前文件的位置(从0开始算)
ARGV 包含命令行参数的数组
CONVFMT 数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组
ERRNO 最后一个系统错误的描述
FIELDWIDTHS 字段宽度列表(用空格键分隔)
FILENAME 当前文件名
FNR 各文件分别计数的行号
FS 字段分隔符(默认是任何空格)
IGNORECASE 如果为真,则进行忽略大小写的匹配
NF 一条记录的字段的数目
NR 已经读出的记录数,就是行号,从1开始
OFMT 数字的输出格式(默认值是%.6g)
OFS 输出记录分隔符(输出换行符),输出时用指定的符号代替换行符
ORS 输出记录分隔符(默认值是一个换行符)
RLENGTH 由match函数所匹配的字符串的长度
RS 记录分隔符(默认是一个换行符)
RSTART 由match函数所匹配的字符串的第一个位置
SUBSEP 数组下标分隔符(默认值是/034)
$ more awk.txt 
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
$ awk '{print NR,NF,$1,$2,$3}' awk.txt 
1 5 2 this is
2 5 3 Are you
3 3 This's a test
4 4 10 There are

七 内置函数

   内置的字符串函数

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的后缀部分

  split 分割字符串 允许你把一个字符串分隔为单词并存储在数组中。你可以自己定义域分隔符或者使用现在FS(域分隔符)的值。
格式:

   split (string, array, field separator)
   split (string, array)  -->如果第三个参数没有提供,awk就默认使用当前FS值。

$ echo time='12:34:56'
time=12:34:56
$ echo time='12:34:56'|awk '{split($0,a,":");print a[1],a[2],a[3]}'
time=12 34 56

substr 截取字符串     返回从起始位置起,指定长度之子字符串;若未指定长度,则返回从起始位置到字符串末尾的子字符串。
格式:
  substr(s,p) 返回字符串s中从p开始的后缀部分
  substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部分

$  echo "abcd" | awk '{print substr($0,2,2)}'
bc

length 字符串长度
   length函数返回没有参数的字符串的长度。length函数返回整个记录中的字符数。

 echo "bohu83" | awk '{print length}'
6

八 正则表达式

 

# 输出包含"re" 的行
$  awk '/re/ ' awk.txt
3 Are you like awk
10 There are orange,apple,mongo

九 流程控制

 9.1 begin /end

在awk 中两个特别的表达式,BEGIN和END,这两者都可用于pattern中(参考前面的awk语法),提供BEGIN和END的作用是给程序赋予初始状态和在程序结束之后执行一些扫尾的工作。任何在BEGIN之后列出的操作(在{}内)将在awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。

举例:统计某个文件夹下的文件占用的字节数

$ ll |awk 'BEGIN{size=0;} {size=size+$5;} END{print "[end]size is ",size/1024/1024,"M"}'
[end]size is  61.9682 M
$ ll -lh
total 62M

9.2 if else

if(表达式

语句1

else

语句2

就是awk支持括号嵌套的复杂判断,但是我们平时用不到,一个if就很常见。

  9.3 循环结构

我们已经看到了 awk 的 while 循环结构,它等同于相应的 C 语言 while 循环。 awk 还有"do...while"循环,它在代码块结尾处对条件求值,而不像标准 while 循环那样在开始处求值。

它类似于其它语言中的"repeat...until"循环。

9.4 for 循环、break\coninue

 上面的跟C语言的类似,抱歉我还没深入的写脚本体验。例子没有。

9.5 数组

一个典型应用,用 awk 中查看服务器连接状态并汇总 

$ netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}'
TIME_WAIT 26
CLOSE_WAIT 2
SYN_SENT 1
ESTABLISHED 20
LISTEN 5

10 实战举例

more order.log 
2018-03-06 00:00:01 049 [WARN] method:IOrderService.getOrder timecost:18ms 
2018-03-06 00:00:01 436 [WARN] method:IOrderService.getOrder timecost:9ms 
2018-03-06 00:00:01 578 [WARN] method:IOrderService.getOrder timecost:40ms 
2018-03-06 00:00:01 600 [WARN] method:IOrderService.getOrder timecost:76ms 
2018-03-06 00:00:02 157 [WARN] method:IOrderService.getOrder timecost:100ms 
2018-03-06 00:00:02 327 [WARN] method:IOrderService.getOrder timecost:140ms 
2018-03-06 00:00:03 327 [WARN] method:IOrderService.getOrder timecost:323ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:1002ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:345ms 
2018-03-06 00:00:01 049 [WARN] method:IOrderService.getOrder timecost:18ms 
2018-03-06 00:00:01 436 [WARN] method:IOrderService.getOrder timecost:9ms 
2018-03-06 00:00:01 578 [WARN] method:IOrderService.getOrder timecost:40ms 
2018-03-06 00:00:01 600 [WARN] method:IOrderService.getOrder timecost:76ms 
2018-03-06 00:00:02 157 [WARN] method:IOrderService.getOrder timecost:100ms 
2018-03-06 00:00:02 327 [WARN] method:IOrderService.getOrder timecost:140ms 
2018-03-06 00:00:03 327 [WARN] method:IOrderService.getOrder timecost:323ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:1002ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:345ms 

输出 接口耗时》300ms的日志。

两种方式:

$ awk -F ':' '{split($5,a,"ms");if (a[1]>300) print $0}' order.log
2018-03-06 00:00:03 327 [WARN] method:IOrderService.getOrder timecost:323ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:1002ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:345ms 
2018-03-06 00:00:03 327 [WARN] method:IOrderService.getOrder timecost:323ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:1002ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:345ms

这种思路就是先用“:”分割,在用ms切分字符串截取ms之前的数字。 还用到了if 判断条件

$ awk  '{split(substr($6,10),a,"ms");if (a[1]>300) print $0}' order.log
2018-03-06 00:00:03 327 [WARN] method:IOrderService.getOrder timecost:323ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:1002ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:345ms 
2018-03-06 00:00:03 327 [WARN] method:IOrderService.getOrder timecost:323ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:1002ms 
2018-03-06 00:00:04 327 [WARN] method:IOrderService.getOrder timecost:345ms

或者根据日志格式,默认用空格“ ”拆分,用到了substr,split, if.

 当然网上也有类似的,在awk前开始先用sed 's/ms//g' 把毫秒ms去了,就不用再处理了。

个人感觉熟练很重要。长时间不用,就很快忘了。做个笔记备用。

猜你喜欢

转载自blog.csdn.net/bohu83/article/details/86165489
今日推荐