awk Detailed

awk is a very fast hardware report generation tools, text can be formatted to appear as a result of more intuitive

Ado, directly on the example of

awk built-in variable

The FS: Field Separator , when reading this document, the use of field delimiter;

The RS: the Record Separator , input text information using line breaks;

OFS: Output Filed Separator: output field separator , a space by default;

The ORS : OutputRow Separator: output record delimiter , the default is a new line;


BEGIN role and END are given initial state to the program and do some mop-up work after the end of the program, the general statement before execution can use BEGIN to declare in advance

BEGIN : preparatory work must be done before the actual processing text, where we can define variables or other operations, and the END rule we defined in the last executed statement is executed as follows:

# Prior statement, first print information "UserName Shell", and print out the first column and the last one

[root@test3 ~]# awk -F: 'BEGIN{print "UserName Shell"}{print $1,$NF}' /etc/passwd | head -3

UserNameShell

root /bin/bash

bin /sbin/nologin

END mode

# First variable BEGIN previously defined default separator is a colon (FS = ":") delimiter field output display line is a colon (the OFS = " : " ), the first column and the last column and the print file, the printing is completed after that, the most the last line of the display contents "END"

[root@test3 ~]#  awk'BEGIN {FS=":";OFS=":"} {print $1,$NF} END{print"end"}' /etc/passwd  | tail -3

mysql:/bin/bash

mockbuild:/bin/bash

end

If the variable value OFS is not specified, the output of a space-separator, as shown below

# Can be found printed output delimiter is a space, which is the default

[root@test3 ~]#  awk'BEGIN {FS=":"} {print $1,$NF} END{print "end"}'/etc/passwd  | tail -3

mysql /bin/bash

mockbuild /bin/bash

end

Regular expression pattern:

Regular expression pattern can be found among the documents matching processing its done and then a row, as shown below

# Specify the default segmentation symbols colon (direct -F: do not bother)

# Specify a default display colon delimiter

# Find a match we had given good ending sh-line and print out of the first column and tail, and finally ending with the End

[root@test3 ~]# awk 'BEGIN{print "UserName Shell"}{FS=":";OFS=":"} /sh$/{print $1,$NF} END {print "End"}' /etc/passwd

UserName Shell

root:x:0:0:root:/root:/bin/bash:root:x:0:0:root:/root:/bin/bash

test:/bin/bash

test2:/bin/bash

mysql:/bin/bash

mockbuild:/bin/bash

End


Do multi-line matching

[root@test3 ~]# awk -F':' 'BEGIN {print "UserNameShell"} {OFS=":"} /sh$/,/in$/ {print$1,$NF} END {print "end"}' /etc/passwd

UserName Shell

root:/bin/bash

bin:/sbin/nologin

test:/bin/bash

test2:/bin/bash

mysql:/bin/bash

mockbuild:/bin/bash

end

awk Analyzing Mode

We want to show the user id number is greater than or equal to 100, and print

# Colon as the delimiter of $ 3 for determining if the value is greater than 100, then print out of the first end of the column and

[root@test3 ~]# awk -F: '$3>100 {print $1,$NF }' /etc/passwd

abrt /sbin/nologin

saslauth /sbin/nologin

test /bin/bash

test2 /bin/bash

mysql /bin/bash

mockbuild /bin/bash

Use awk match

# Colon as the delimiter, if the content appears in column 7 bash field (this is easy to understand), then the print columns 1 and 3

[root@test3 ~]# awk -F: '$7~/bash/ {print $1,$3}' /etc/passwd

root 0

test 500

test2 501

mysql 498

mockbuild 502

Negate

Output of non-users bash

[root@test3 ~]# awk -F ':' '$7!~/bash/ {print $1,$NF}'/etc/passwd


printf

Syntax: printfformat, item1, item2, ...

Highlights:

1 , which is the biggest difference is the print command, printf need to specify the format;

2 , the format of each item for specifying the output format of the latter;

3 , printf statement does not automatically print a newline; \ n

format indicator format begins with a%, followed by a character; as follows:

%c: 显示字符的ASCII码;
%d, %i:十进制整数;
%e, %E:科学计数法显示数值;
%f: 显示浮点数;
%g, %G: 以科学计数法的格式或浮点数的格式显示数值;
%s: 显示字符串;
%u: 无符号整数;
%%: 显示%自身;

修饰符:

N: 显示宽度;
-:
左对齐
+:显示数值符号;

#打印第1列和第3列,并使用修饰符:字符距离为15,类型为整数型并将其换行显示,如果不加参数\n 那么打印出的信息则全显示在一行中

[root@test3 ~]# awk -F: '{printf "%-15s%i\n",$1,$3}' /etc/passwd | head -5

root            0

bin             1

daemon          2

adm             3

lp              4

这里面有2个符号 一个是s% 一个是 i% s%表示显示的是字符串 i%表示显示为十进制的整数 %-15s %i\n 表示使用多宽的字符来显示它 -号表示左对齐

不使用右对齐

[root@test3~]# awk -F: '{printf "%15s %i\n",$1,$3}' /etc/passwd | head 2

          root 0

           bin 1

显示无符号整数:

[root@test3~]# awk -F: '{printf "%15s %u\n",$1,$3}' /etc/passwd

要点:
1、各项目之间使用逗号隔开,而输出时则以空白字符分隔;
2、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;
3、print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print"";


awk的操作符

·算数操作符

-x: 负值
+x: 转换为数值;
x^y:
x**y: 次方
x*y: 乘法
x/y:除法
x+y:
x-y:
x%y:

·字符串操作符:
只有一个,而且不用写出来,用于实现字符串连接;
·赋值操作符:

=
+=
-=
*=
/=
%=
^=
**=


++
--

需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代;

·布尔值

awk中,任何非0值或非空字符串都为真,反之就为假;

·比较操作符:

x <y              True ifx is less than y.
x <= y             Trueif x is less than or equal to y.
x > y              Trueif x is greater than y.
x >= y             Trueif x is greater than or equal to y.
x == y             Trueif x is equal to y.
x != y             Trueif x is not equal to y.
x ~ y              Trueif the string x matches the regexp denoted by y.
x !~ y             Trueif the string x does not match the regexp denoted by y.
subscript in array       True if the array array hasan element with the subscript subscript.

·表达式间的逻辑关系符:

&&

||

比如:

#如果第3列的数值大于400 并且第7列匹配bash字符串那么打印1 3 7列 并修饰

[root@test3~]#  awk -F ':' '$3>400 &&$7~/bash/{printf "%-15s %i %s\n",$1,$3,$7}' /etc/passwd

test            500 /bin/bash

test2           501 /bin/bash

mysql           498 /bin/bash

mockbuild       502 /bin/bash

awk常用分隔符

一般常用分隔符分为两类:

(1)字段分隔符:某行中如何区分不同的列

(2)行分隔符:默认情况下就是换行符,完全指定别的分割符为换行符,这样可以将一行当多行或 多行当一行来处理

所以一般来将字段分隔符的输入分割符,我们被称为FS,而FS恰好就是awk内置变量

上面已经介绍了awk的内置变量FS和OFS的使用,接下来我们来回顾一下

FS:指定输入字段分隔符

#指定以冒号为分割符号,打印列首和列尾

[root@test3~]# awk 'BEGIN{FS=":"} {print $1,$NF}' /etc/passwd | head -3

root/bin/bash

bin/sbin/nologin

daemon/sbin/nologin


OFS:指定输出字段分割符

#指定以冒号为分割符号,指定##为默认输出分割符(这里不再是空格),打印列首和列尾

[root@test3~]# awk 'BEGIN{FS=":";OFS="##"} {print $1,$NF}' /etc/passwd| head -3

root##/bin/bash

bin##/sbin/nologin

daemon##/sbin/nologin


NF

#引用变量本身的值是不可以加$的 这里加$是因为用变量本身的函数再补以$符号

[root@test3~]# df -h | awk 'BEGIN {print "fliesystem mount" } {print $1,$NF}'

加$符号,打印出的结果,筛选为第5列

[root@test3~]# awk -F ':' 'BEGIN{TEST=5} {print $TEST}' /etc/passwd  | head -5

root

bin

daemon

adm

lp

再将$符号去掉可以看到只将TEST赋予的值打印了出来

[root@test3~]# awk -F ':' 'BEGIN{TEST=5} {print TEST}' /etc/passwd  | head -5

5

5

5

5

5

控制语句

if-else

语法:if(condition) {then-body} else ` else-body `

我们可以使用单分支if语句 只有if没有else

#判断,如果这个用户的id号是否为0,如何是0则输出为管理员反之并打印其用户名

[root@test3~]# awk -F: '{if($3==0){print $1,"admin"}else{print$1,"user"}}' /etc/passwd | head -3

rootadmin

binuser

daemonuser

使用修饰符

[root@test3~]# awk -F: '{if($3==0){printf "%-15s%s\n",$1,"admin"}else{printf "%-15s %s\n",$1,"user"}}' /etc/passwd | head -3

root            admin

bin             user

daemon          user


用户的自定义变量

参数:-v

#如果用户的id号大于等于500那么使变量在其自身+1次并打印,最终所有大于500的用户的个数的和

#print必须写在END后面,不然会依次显示行的处理结果

[root@test3~]# awk -F: -v sum=0 '{if($3>500) sum++} END {print sum}' /etc/passwd

2

这里sum没有加$,因为$显示的变量值是对应的字段的值,如下所示:

[root@test3~]# awk -F: -v sum=0 '{if ($3>500) sum++} END{print $sum}'/etc/passwd
x

分别打印id大于500的用户名

[root@test3~]# awk -F: '{if ($3>=500) print $1}' /etc/passwd
test
test2
mockbuild

使用awk统计登录失败的ssh登录信息

#首先找到关键字,其实用grep+管道+awk更直观一些,这里为了练习就全部都用awk了

#看到有3行此类信息,然后对其统计有多少条匹配的信息

[root@test3~]# awk '/Failed/' /var/log/secure

Feb 1116:48:40 test3 sshd[2015]: Failed password for invalid user adad from 10.0.10.1port 58136 ssh2

Feb 1116:48:44 test3 sshd[2015]: Failed password for invalid user adad from 10.0.10.1port 58136 ssh2

Feb 1116:48:47 test3 sshd[2015]: Failed password for invalid user adad from 10.0.10.1port 58136 ssh2

#使用-v声明变量值为0,{sum++}必须单独在一{..}模块中,在结尾处打印变量

[root@test3~]# awk -v sum=0 '/Failed/ {sum++} END {print sum}' /var/log/secure

3

筛选web日志文件,并且将以GET请求后缀名是以.html结尾的行进行访问数统计

[root@test3logs]# awk  '{if($7 ~ "html$")print $0}' access_www.test.com.log-20140209

10.0.10.1- - [04/Feb/2014:10:40:07 +0800] "GET /1.html HTTP/1.1" 200 6

10.0.10.1- - [04/Feb/2014:10:40:11 +0800] "GET /2.html HTTP/1.1" 200 5

10.0.10.62- - [04/Feb/2014:10:40:37 +0800] "GET /1.html HTTP/1.1" 200 6

10.0.10.62- - [04/Feb/2014:10:40:41 +0800] "GET /2.html HTTP/1.1" 200 5

10.0.10.62- - [04/Feb/2014:10:40:45 +0800] "POST /2.html HTTP/1.1" 200 5

10.0.10.62- - [04/Feb/2014:10:40:51 +0800] "POST /1.html HTTP/1.1" 200 6

10.0.10.1- - [04/Feb/2014:10:48:22 +0800] "GET /1.html HTTP/1.1" 200 6

可以看到一共有7行,下面来使用awk来统计其访问量

[root@test3logs]# awk -v sum=0 '{if($7 ~ "html$") sum++} END {print sum}'access_www.test.com.log-20140209

7


循环

while循环

主要功能在切片后每一个字段中进行循环

语法:while (condition) {statement1;statement2;...}

#显示没行的前三段

[root@test3logs]# awk -F: '{i=1;while (i<=3) {print $i;i++} }'/etc/passwd | head -6

root

x

0

bin

x

1

#引用变量不需要加$

#每个处理结果都是一行,所以是按行输出的

#如果不想让其按行输出则:

[root@test3logs]# awk -F: '{i=1; while(i<=3) {printf "%s ", $i;i++};{printf"\n"} }' /etc/passwd


awk内置函数length

length能取出指定字符的长度

#显示每行中数字大于等于100的数字

[root@test3~]# cat hello.txt
111 11 234 99 87

130 8328 91

2384842 84 671 24
87 62 1992

#之所以使用i<=NF是因为每个字段的数值是不一样的,但是这里每一列的个数都不一样的,为的目的是便利每一行的每一列

[root@test3~]# awk '{i=1;while (i<=NF) {if($i>=100) print $i;i++ }}' hello.txt

111                                  

234

130

2384

842

671

1992


do-while 循环

其与while区别在于 while有可能一开始就不会循环,因为条件不满足

而do-while则不管值为真假(不管条件满足与否,至少先执行一次循环),先进行循环

语法: do {statement1, statement2, ...} while (condition)

awk -F:'{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd

awk -F:'{i=4;do {print $i;i--}while(i>4)}' /etc/passwd


for循环

语法 : for (variable assignment; condition iteration process ) {statement1 ... }

#以冒号为分割符,显示每行的前三列,与上面的while循环的结果是一样的

[root@test3~]# awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd | head -10

root

x

0

bin

x

1

daemon

x

2

使用内置函数length

#awk-F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd

使用for循环来查找这个文件中大于等于100的数字

#一定让i小于NF,如果$i大于等于100 那么现实$i  

#大致意思为我们要读取这个文件的每一行,而后进行每一列进行判断,而for是对其进行每一列来进行循环的,if为判断每一列只要大于其值则打印

[root@test3~]# awk '{for(i=1;i<=NF;i++) {if ($i>=100) print $i}}' hello.txt

111

234

130

2384

842

671

1992


数组

#数组为一组连续的课存多个值内存空间

#使用数组索引(下标)

#例:

bash:

arry=('mon''tue' 'wed')

arry[2]

这里的arry[2]就为数组的索引,但是bash4之后还支持关联数组:

索引下标可以非数组,可以自定义下标:

arry=(a='mon'b='tue')

aryy[b]

而awk是支持所谓的关联数组的

for能够去便利数组元素:

语法: for (i in array) {statement1,statement2, ... }

统计passwd文件最后一列出现的次数

[root@test3~]# awk -F: '$NF!~/^$/{BASH[$NF]++}END{for (A in BASH) {printf"%-15s:%i\n" ,A,BASH[A]}}' /etc/passwd

/sbin/shutdown:1

/bin/bash      :5

/sbin/nologin  :22

/sbin/halt     :1

/bin/sync      :1

#首先$NF 为最后一个字段

#!~为不匹配,这里不为空

#如果最后一列不为空,则将其值取出并对其做匹配:

BASH[] 为数组 那么BASH[NF]为数组索引,可以将最后一个字段的字段本身,将其作为下标

比如:

最后一列为 /bin/bash,那么将其做为BASH的下标

所以会出现这样结果:

BASH[/bin/bash]

BASH[/sbin/nologin] 用它来做下标即为另一个元素,那么这个元素中存放的是什么也没有声明

所以这里BASH[$NF]++ 使其自身自动加值,一般初始值是0,所以 它的意思为 如果不为空,则BASH[NF]++ :

当我们读了第一行之后意味着BASH[/bin/bash]的元素为=1 ,

再读取第二行于是又一个新元素其BASH[/sbin/nologin]的值=1

再读取第三行,筛取最后一列为/sbin/nologin其 BASH[/sbin/nologin]的值+1=2

继续。。。每一次都往上累加

#

{print "%15s:%i\n",A,BASH[A]}}

#显示了A,再显示了BASH[A]

A是字符串

BASH[A]为元素的值

得出的结果为:

/bin/bash : 1

/sbin/nologin:1 ....


#使用netstate -tanl查看网络状态并统计每种状态的值

[root@test3~]# netstat -tanl | awk '/^tcp/{sum[$NF]++} END {for (i in sum) {printi,sum[i]}}'

TIME_WAIT61

ESTABLISHED3

LISTEN8

语法说明:

awk /^tcp/{动作}  #将tcp协议开头的行筛选并打印出来

awk /^tcp/{sum[$NF]}   #$NF的值一定是字符串本身

awk /^tcp/{sum[$NF]++} #第一次状态值为1 第二次为2 以此类推


#for(下标 in 数组)

awk/^tcp/{sum[$NF]++}END{for(i in sum)}                  #使用for循环,会便利数组的下标,所以我们显示的是

awk/^tcp/{sum[$NF]++}END{for(i in sum) print i,sum[i]}'  #i是字符串本身,sum[i] 数组的以i为下标的数组的值

所以得出的结果为:

[root@test3~]# netstat -tanl | awk '/^tcp/{sum[$NF]++} END {for (i in sum) {printi,sum[i]}}'

ESTABLISHED3

LISTEN8

统计当前系统上的日志access.log 每一个ip发起过多少次请求

[root@test3logs]# awk '{ip[$1]++} END {for(i in ip) print i,ip[i]}'access_www.test.com.log

127.0.0.161

next

提前结束对本行文本的处理,并接着处理下一行;例如,下面的命令将显示其ID号为奇数的用户:

[root@test3logs]# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd

bin 1

adm 3

sync 5

halt 7

operator11

gopher13

nobody99

dbus 81

vcsa 69

abrt173

saslauth499

postfix89

named25

test2501


awk内置函数

语法:split(string, array [,fieldsep [, seps ] ])

功能:将string表示的字符串以fieldsep为分隔符进行分隔,并将分隔后的结果保存至array为名的数组中;数组下标为从1开始的序列;

[root@test3logs]#  netstat -tan | awk'/:80\>/{split($5,clients,":");ip[clients[4]]++}END{for(a in ip)print ip[a],a}' | sort -rn | head -50

9010.0.10.62

1 *

语法说明:

awk '/:80\>/

{split($5,clients,":");     #当我们取得第五列的时候,将第五列以冒号作为分割(":" 为分隔符),保存在名为clients的变量中,意味着clients1保存的是我们的ip地址,clients2保存的是端口号.....

IP[clients[1]]++}        #ip地址+1

END{for(i in IP)         #便利每个IP显示的次数

{print IP[i],i}}'        #先显示次数再显示地址


筛取硬盘空间使用比例大于20%

[root@test3logs]# df -h

Filesystem            Size  Used Avail Use% Mounted on

/dev/sda3              20G   13G 5.3G  72% /

tmpfs                 498M     0 498M   0% /dev/shm

/dev/sda1             194M   45M 140M  25% /boot

/dev/mapper/myvg-mydata

                     5.0G  170M 4.6G   4% /mydata

[root@test3logs]# df -hl | awk'!/File/{split($5,percent,"%");if(percent[1]>=20) {print $1}}'

/dev/sda3

/dev/sda1