借鉴别人写的很好的文章:
https://www.cnblogs.com/ggjucheng/archive/2013/01/13/2858470.html
https://www.cnblogs.com/ginvip/p/6352157.html
awk是文本文件的处理工具,主要是进行过滤和搜索,对文件文件的内容通过换行符进行分隔,作为一条记录,,然后将每一条记录默认是以空格为分割符来划分为分隔域,第一个域是$1,第二个是$2,依次类推,其中$0表示整个域,然后对分隔后的数据一条一条的处理,这里和sed进行区分,sed是默认以换行符为分隔符。
语法:
awk ‘{pattern + action}’ {filenames}
这里的action可以有多条语句,多条语句之间用;分隔开,后面会介绍。
eg:
awk -F: 'BEGIN{count=0}{count+=1;print $0}END{printf('count:%s',count)}'
可以看到{count+=1;print $0}中有两个语句,两个语句之间使用;进行分割开来;
第一种用法:
cat /etc/passwd |awk -F ':' 'BEGIN {print "name,shell"} {print $1","$7} END {print "blue,/bin/nosh"}'
name,shell
root,/bin/bash
daemon,/bin/sh
bin,/bin/sh
sys,/bin/sh
....
blue,/bin/nosh
awk工作流程是:先执行BEGIN,执行完之后,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,
1
表
示
第
一
个
域
,
1表示第一个域,
1表示第一个域,n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。
第二种用法:只有action的
cat /etc/passwd | awk ‘{print $1}’
其中’{print $1}'是action的内容
第三种:只有匹配模式pattern的,匹配之后直接输出:
[root@uc201 ~]# cat /etc/passwd | awk '/root/'
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
第四种:awk + pattern + action
[root@uc201 ~]# cat /etc/passwd | awk -F: '/root/{print $7}'
/bin/bash
/sbin/nologin
上面的命令大意:匹配有root的行,并将行按照:进行分隔域,分隔之后执行actiion:打印$7。
上面的选项分隔域-F:和-F ":"两种写法都可以。
awk中提供了打印的两个函数,print和printf,print 后面可以根变量,字符串,数值,变量之间可以用逗号但是,表示的是空格,字符串必须用双引号括起来。printf和c语言中一样,支持格式化字符串,如下:
[root@uc201 ~]# awk -F: '{printf("%10s%30s\n",$1,$7)}' /etc/passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
shutdown /sbin/shutdown
halt /sbin/halt
mail /sbin/nologin
operator /sbin/nologin
games /sbin/nologin
[root@uc201 ~]# awk -F: '{printf("%10s,%30s\n",$1,$7)}' /etc/passwd
root, /bin/bash
bin, /sbin/nologin
daemon, /sbin/nologin
adm, /sbin/nologin
lp, /sbin/nologin
print的用法:
[root@uc201 ~]# awk -F: '{print $1,$7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
下面来详细介绍AWK编程:
一、变量
和其他编程语言一样,awk离不开变量,变量分为内置变量和用户自定义变量。内置变量有特殊意义,如下:
awk有内置变量,还可以自定义变量,其中内置变量如下:
ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行 -F选项
NF 浏览记录的域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符
使用:
[root@uc201 ~]# awk -F ':' '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd
filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash
filename:/etc/passwd,linenumber:2,columns:7,linecontent:bin:x:1:1:bin:/bin:/sbin/nologin
filename:/etc/passwd,linenumber:3,columns:7,linecontent:daemon:x:2:2:daemon:/sbin:/sbin/nologin
filename:/etc/passwd,linenumber:4,columns:7,linecontent:adm:x:3:4:adm:/var/adm:/sbin/nologin
filename:/etc/passwd,linenumber:5,columns:7,linecontent:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
用户自定义变量:
[root@uc201 ~]# awk -F: 'BEGIN{count=0} /root/{count++;print $1} END{printf("count is:%8s\n",count)}' /etc/passwd
root
operator
count is: 2
也可以没有BEGIN对count进行初始化,默认就是0,建议初始化。
再给个例子,里面涉及了计算:
[root@uc201 ~]# ll | awk 'BEGIN{size=0} {size=size+$5} END{print "total size:"size/1024/1024"M" }'
total size:935.212M
这里注意的是:print 后面要是打印字符串的话,需要双引号,变量和双引号之间可以不用空格,如下,单引号会报语法错误。
[root@uc201 ~]# ll | awk '{print ':'$1}'
awk: cmd. line:1: {print :$1}
awk: cmd. line:1: ^ syntax error
[root@uc201 ~]#
[root@uc201 ~]#
[root@uc201 ~]# ll | awk '{print ":"$1}'
:总用量
:-rw-r--r--.
:-rw-------.
:-r--r--r--.
awk中有变量,那就离不开变量的对变量的运算,下图给出了变量的运算符:包括赋值运算符、逻辑运算符、关系运算符、算数运算符等
awk支持正则,如下:
有了变量,那么下一步就是语句了,awk也支持条件语句.
来讲下数组吧:
awk中也有数组的概念,数据的索引可以是数字和字母,和c语言就不太一样了,c语言中的索引只能是数字,awk中数组的存储是按照key/value这样的键值对来存储的,感觉和python中的字典有点像,这里不是按照顺序存储的,所以打印出来显示的话也不是每次顺序都一样,这其实就和字典是一样的了,python中的字典每次打印的话顺序也不是固定的。
举个例子:
netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}'
输出结果
ESTABLISHED 1
LISTEN 20
下面来介绍字符串:这里一定要强调下,字符串是双引号,不能用单引号,不然awk会解析为空白:
例如下面这样:
echo "" | awk 'BEGIN{city[1]='hefei';city[2]='hangzhou'}END{for(i in city)print i,city[i]}
上面的输出结果是:
1
2
我就纳闷了,为啥数组内容没有打印出来,原来是前面的赋值的时候不能用单引号。后来改成下面这样就OK了。
echo "" | awk 'BEGIN{city[1]="hefei";city[2]="hangzhou"}END{for(i in city)print i,city[i]}
运行结果:
1 hefei
2 hangzhou
下图是字符串常用的函数:
二、结构化语句
①条件语句
if else语句在awk中的应用:
下面是if else的语法
if (expression) {
statement;
statement;
... ...
}
if (expression) {
statement;
} else {
statement2;
}
if (expression) {
statement1;
} else if (expression1) {
statement2;
} else {
statement3;
}
上面的语句用在awk的action中,直接放进去,当做一个命令。
如下;
[root@uc201 ~]# ll | awk '{if($5<4096){size=size+$5};print $5} END{printf("total size = %s\n",size)}'
76
12232
793
2535
170
980215078
12754
396776
19
total size = 3593
②循环语句
循环语句在awk中应用:
支持while、do/while、for、break、continue,这些关键字的语义和C语言中的语义完全相同。和上面的if一样,都把它当成一个语句就行了,放的位置就是{}这个里面。
[root@uc201 ~]# awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;} END{for (i=0;i<count;i++){printf("%d %10s\n",i,name[i])}}' /etc/passwd
0 root
1 bin
2 daemon
3 adm
4 lp
5 sync
6 shutdown
7 halt
8 mail
9 operator
10 games
11 ftp
12 nobody
13 systemd-network
14 dbus
15 polkitd
16 unbound
17 libstoragemgmt
18 rpc
19 gluster
20 saslauth
21 abrt
22 postfix
23 qemu
24 radvd
25 rpcuser
26 nfsnobody
27 tss
28 sshd
29 openvswitch
30 ntp
31 chrony
32 admin
33 etcd
其实用起来很简单,就是遇到awk命令,先对文本内容进行按照换行符进行分割成一个一个记录,接着读取记录,然后看awk后面有没有设置分割域,有的话,就使用设置的作为分割域,没有就按照默认的空格为分割域,然后对每个分割域执行操作,如何操作呢,需要看是不是有匹配模式的语句,没有的话,就全部匹配,有的话按照设置的进行匹配,可以是正则表达式进行匹配,匹配之后,对匹配的内容进行操作,操作的内容就是一些命令,包裹在{}中,可以有多个命令,命令和命令之间用;分割开,一条一条从前往后之后执行完{}中的命令之后,然后对第二个记录再按照匹配,执行命令,直到最后所有的记录都执行完。
如果在匹配和命令之前和之后还有BEGIN和END,那就第一次执行第一条记录前执行BEGIN中的内容,最后执行完最后一条记录之后,执行END后面的内容,就结束了。
之前我怎么都没懂,现在回过头来好像没那么难。。。