awk简介
本质:
是一门编程语言,有自己的语法和库函数。
工作机理:
读取每一行
-
按分隔符把这一行切成多个(不指定分隔符的话,空白或者连续空白就是分隔符)
$1:代表第一列;$2:第二列。。。。
$0:整行内容
按需,按特定格式打印出来
功能:
- 可以限定处理哪些行
- 可以根据列的内容做条件分支处理
- 可以循环所有列
- 可以自己定义变量
命令基本用法:
awk [option] 'program' file...
programe:patern{action statements}
例子1:/etc/fstab文件用空白分隔,打印出第二列和第四列。
列之间用逗号分隔,打印出来的列之间就有空格;不加逗号,就把这2列连一起了
[root@localhost ~]# tail -4 /etc/fstab /dev/mapper/centos-root / xfs defaults 0 0 uuid=3d3b316a-529e-484a-9895-e785fdde5365 /boot xfs defaul /dev/mapper/centos-home /home xfs defaults 0 0 /dev/mapper/centos-swap swap swap defaults 0 0 [root@localhost ~]# tail -4 /etc/fstab | awk '{print $2,$4}' / defaults /boot defaults /home defaults swap defaults [root@localhost ~]# tail -4 /etc/fstab | awk '{print $2 $4}' /defaults /bootdefaults /homedefaults swapdefaults
例子2:/etc/fstab文件用空白分隔,打印出第二列和第四列,并随意加几列
# tail -4 /etc/fstab | awk '{print "hi",$2,$4,567}' hi / defaults 567 hi /boot defaults 567 hi /home defaults 567 hi swap defaults 567
由上面2个小例子,可以看出awk的基本使用要点:
- print的项目间,加了逗号,打印出来的项目之间就有空格
- print后面的内容,可以是列号,自己定义的变量,随意的字符串和数字
- print后面如果省略项目,就是打印$0
选项
- -f:告诉awk用什么分隔符,去分隔输入
- -v:自定义变量。var=value。定义多个变量要使用多次-v
awk的变量
1,内置变量
-
fs:input file seperator 输入列分隔符,默认是空白或者连续空白
# tail -4 /etc/fstab | awk -v ofs=',' '{print $3,$4}' xfs,defaults xfs,defaults xfs,defaults swap,defaults
-
ofs:output file seperator 输出列分隔符,默认是空白或者连续空白
# tail -4 /etc/passwd | awk -v fs=':' '{print $1,$4}' us3 1002 user100 1003 user101 1004 apache 48
# tail -4 /etc/passwd | awk -v fs=':' -v ofs=':' '{print $1,$4}' us3:1002 user100:1003 user101:1004 apache:48
rs:input record seperator 输入行识别符,默认是回车
ors:output record seperator 输出行识别符,默认是回车
-
nf:每行里列的数量
在awk内部引用变量不需要加$。
# awk '{print nf}' /etc/fstab 0 1 2 10 1 9 12 1 6 6 6 6
-
nr:文件的行号
# awk '{print nr}' /etc/fstab 1 2 3 4 5 6 7 8 9 10 11 12
如果是多个文件,则连续计数行号。
# awk '{print nr}' /etc/fstab /etc/issue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-
fnr:如果是多个文件,则单独计数行号。
# awk '{print fnr}' /etc/fstab /etc/issue 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3
-
filename:显示文件名
# awk '{print filename}' /etc/fstab /etc/issue /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/fstab /etc/issue /etc/issue /etc/issue
argc:命令行参数的个数
-
argv:数组,保存的是接收到的命令行参数。
# awk '{print argc, argv[0],argv[1],argv[2]}' /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue # awk 'begin{print argc, argv[0],argv[1],argv[2]}' /etc/fstab /etc/issue 3 awk /etc/fstab /etc/issue
2,自定义变量
变量名区分大小写,可以使用-v定义;也可以在program体里定义。什么是program,在大括号里的部分。programe里的多个语句,使用分号分隔。
-
使用-v定义变量
# awk -v var="122dd" '{print var}' /etc/issue 122dd 122dd 122dd
-
在在program体里定义变量
# awk '{var="11d";print var}' /etc/issue 11d 11d 11d
printf语句用法
实现格式化输出。不自动换行,使用\n是换行。
格式符:
- %c:显示ascii码
- %d,%i:显示十进制整数
- %e, %e:显示科学计数法
- %f:显示浮点数
- %g, %g:显示科学计数法或浮点数
- %s:显示字符串
- %u:显示无符号整数
- %%:显示%自身
# awk -f: '{printf "username:%s,uid:%d\n",$1,$3}' /etc/passwd username:avahi,uid:70 username:postfix,uid:89
修饰符:
- 数字1[.数字2]
- -数字1[.数字2]:左对齐
- +数字1[.数字2]:显示数字的符号,正数显示+,符号显示-
数字1:显示时,列所占用的宽度。默认是右对齐,-号是左对齐。
数字2:有小数点的话,指定显示几位小数。
# awk -f: '{printf "username:%15s,uid:%d\n",$1,$3}' /etc/passwd username: user101,uid:1004 username: apache,uid:48 # awk -f: '{printf "username:%-15s,uid:%d\n",$1,$3}' /etc/passwd username:user100 ,uid:1003 username:user101 ,uid:1004 username:apache ,uid:48
操作符
1,算术操作符
a+b,a-b,a*b,a/b,a^b,a%b,
2,字符串操作符
没有操作符,就是连接字符串
3,赋值操作符
=,+=,-=,*=,/=,%=,^=
++,--
4,比较操作符
>,>=,<,<=,!=,==
5,模式匹配符
~:左侧的字符串是否能被右侧的模式所匹配
!~:左侧的字符串是否能被右侧的模式所不匹配
6,逻辑操作符
&&,||,!
7,函数调用
function_name(arg1,arg2...)
8,条件表达式
selector?true:false
例子:uid大于等于1000的是aa。
# awk -f: '{$3>=1000?ut="aa":ut="bb"; printf "%s:%d\n", ut, $3}'\n /etc/passwd
pattern
选择要处理的行,不写pattern默认是处理所有行。
1,/正则表达式/:仅把被匹配到的行作为处理对象
仅处理uuid开头的行
# awk -f' ' '/^uuid/{print $1, $3}'\n /etc/fstab uuid=3d3b316a-529e-484a-9895-e785fdde5365 xfs
a仅处理不以uuid开头的行
# awk -f' ' '!/^uuid/{print $1, $3}'\n /etc/fstab # # # by # # filesystems, # man # /dev/mapper/centos-root xfs /dev/mapper/centos-home xfs /dev/mapper/centos-swap swap
2,关系表达式:结果为真的行,才是处理对象。结果是非0或非空白都是真。
处理uid大于等于1000的行
# awk -f: '$3>=1000{print $1,$3}' /etc/passwd nfsnobody 65534 us1 1001 us3 1002 user100 1003 user101 1004
处理shell是bash的行
最后一列的字符串包含bash的行 # awk -f: '$nf~"bash"{print $1,$nf}' /etc/passwd root /bin/bash ys /bin/bash us1 /bin/bash us3 /bin/bash user100 /bin/bash user101 /bin/bash 最后一列的字符串以bash结尾的行 # awk -f: '$nf~/bash$/{print $1,$nf}' /etc/passwd root /bin/bash ys /bin/bash us1 /bin/bash us3 /bin/bash user100 /bin/bash user101 /bin/bash # awk -f: '$nf=="/bin/bash"{print $1,$nf}' /etc/passwd root /bin/bash ys /bin/bash us1 /bin/bash us3 /bin/bash user100 /bin/bash user101 /bin/bash
指定处理的行号
# awk -f: 'nr>=1&&nr<=3{print $1,$nf}' /etc/passwd root /bin/bash bin /sbin/nologin daemon /sbin/nologin
指定处理的开始行,终了行,但不是用数字,而是用pattern
# awk -f: '/^root/,/^daemon/{print $1,$nf}' /etc/passwd root /bin/bash bin /sbin/nologin daemon /sbin/nologin
3,begin,end
- begin:处理第一行前,执行一次begin。一般用于打印表头。
- end:处理完最后一行后,执行一次end。一般用于打印汇总。
# awk -f: 'begin{print " username uid"} {print $1,$3}' /etc/passwd username uid root 0 bin 1 daemon 2
打印出前5行的username和uid,并在结尾统计出:uid之和
# awk -f: -v sum=0 'begin{print "username uid"} nr>=1&&nr<=5{printf "%8s %3d\n", $1,$3;sum+=$3} end{printf " sum:%s\n",sum}' /etc/passwd username uid root 0 bin 1 daemon 2 adm 3 lp 4 sum:10
常用的action
1,控制语句
-
if(condition) {...}
仅显示uid大于等于1000的行。
# awk -f: '{ if($3>=1000) {print $1,$3}}' /etc/passwd nfsnobody 65534 ys 1000 us1 1001 us3 1002 user100 1003 user101 1004
-
if(condition) {...} else {...}
# awk -f: '{ if($3>=1000) {printf "user:%s\n",$1} else {printf "suser:%s\n",$1} }' /etc/passwd suser:root user:nfsnobody
例子:统计磁盘使用量超过10%的分区。
按空格分隔,用第五列做判断,第五列是use%,17%,0%等,可以直接和10作比较。
其实原理是字符串比较,字母的ascii码大于数字,所以第一行也显示出来了,完美实现要求。
# df -h filesystem size used avail use% mounted on /dev/mapper/centos-root 38g 6.1g 31g 17% / devtmpfs 1.9g 0 1.9g 0% /dev tmpfs 1.9g 0 1.9g 0% /dev/shm tmpfs 1.9g 9.0m 1.9g 1% /run tmpfs 1.9g 0 1.9g 0% /sys/fs/cgroup /dev/sda1 1014m 179m 836m 18% /boot /dev/mapper/centos-home 19g 40m 19g 1% /home tmpfs 379m 0 379m 0% /run/user/0 # df -h | awk '{if($5>"10") print $0}' filesystem size used avail use% mounted on /dev/mapper/centos-root 38g 6.1g 31g 17% / /dev/sda1 1014m 179m 836m 18% /boot
-
switch(expression){case val1 or /pattern/: 语句; case val1 or /pattern/ 语句; ... default: 语句}
val是固定的值;pattern是正则表达式。
-
while(condition) {...}
统计/etc/fstab文件,u和/开头的行是处理对象。把每列的字符串的长度算出来,显示在列的后面,中间用冒号分隔。循环到最后一列时,打印换行。
# awk '/^[u\/]/{i=1;while(i<=nf) { printf "%s:%d ",$i,length($i); if(i==nf) {printf "\n"} i++} }' /etc/fstab /dev/mapper/centos-root:23 /:1 xfs:3 defaults:8 0:1 0:1 uuid=3d3b316a-529e-484a-9895-e785fdde5365:41 /boot:5 xfs:3 defaults:8 0:1 0:1 /dev/mapper/centos-home:23 /home:5 xfs:3 defaults:8 0:1 0:1 /dev/mapper/centos-swap:23 swap:4 swap:4 defaults:8 0:1 0:1
do{...} while(condition)
-
for(expr1;expr2;expr3){...}
统计/etc/fstab文件,u和/开头的行是处理对象。把每列的字符串的长度算出来,显示在列的后面,中间用冒号分隔。循环到最后一列时,打印换行。
# awk '/^[u\/]/{for(i=1;i<=nf;i++) { printf "%s:%d ",$i,length($i); if(i==nf) {printf "\n"} } }' /etc/fstab /dev/mapper/centos-root:23 /:1 xfs:3 defaults:8 0:1 0:1 uuid=3d3b316a-529e-484a-9895-e785fdde5365:41 /boot:5 xfs:3 defaults:8 0:1 0:1 /dev/mapper/centos-home:23 /home:5 xfs:3 defaults:8 0:1 0:1 /dev/mapper/centos-swap:23 swap:4 swap:4 defaults:8 0:1 0:1
遍历数组的特殊用法:
for(var in array)
continue,bread[n](退出哪层循环)
-
next:提前退出当前行的处理,直接进入下一行。类似continue。
显示uid是偶数的行。
# awk -f: '{ if($3%2)next;print $1,$3}' /etc/passwd root 0 daemon 2 lp 4 shutdown 6
delete arrary[index]:删除数组的某个元素
delete array:删除数组
exit:退出
2,input statements
3,output statements
数组
- 索引数组:索引是数字
- 关联数组:索引是任意的字符串,字符串要用双引号括起来。
当引用一个不存在的数组元素时,会自动创建之。数组索引是从1开始。
判断某个索引是否存在,要使用特殊的语法:index in array
第一个wk["tur"]=12222a的12222a没有用双引号括起来,打印wk["tur"]的值变成了12222,自动把a去掉了。
# awk 'begin{wk["mon"]="aaa";wk["tur"]=12222a; print wk["tur"]; print wk["mon"]}' 12222 aaa [root@localhost ~]# awk 'begin{wk["mon"]="aaa";wk["tur"]="1222a"; print wk["tur"]; print wk["mon"]}' 12222a aaa
用for遍历数组,i是数组的索引,而不是数组元素的值。
# awk 'begin{wk["mon"]="aaa";wk["tur"]="12222a"} {for(i in wk)print i}' /etc/issue tur mon tur mon tur mon [root@localhost ~]# awk 'begin{wk["mon"]="aaa";wk["tur"]="12222a"} {for(i in wk)print wk[i]}' /etc/issue 12222a aaa 12222a aaa 12222a aaa
统计每个状态出现的次数之和。
# netstat -tan active internet connections (servers and established) proto recv-q send-q local address foreign address state tcp 0 0 0.0.0.0:111 0.0.0.0:* listen tcp 0 0 192.168.122.1:53 0.0.0.0:* listen tcp 0 0 0.0.0.0:22 0.0.0.0:* listen tcp 0 0 127.0.0.1:631 0.0.0.0:* listen tcp 0 0 127.0.0.1:6011 0.0.0.0:* listen tcp 0 64 192.168.56.107:22 192.168.56.1:60328 established tcp6 0 0 :::111 :::* listen tcp6 0 0 :::80 :::* listen tcp6 0 0 :::22 :::* listen tcp6 0 0 ::1:631 :::* listen tcp6 0 0 ::1:6011 :::* listen # netstat -tan | awk '{if(nr==1 || nr==2)next; wk[$6]++} end{for(i in wk) printf "%s:%d times\n", i,wk[i]}' listen:10 times established:1 times
函数
1,内置函数
rand():随机返回小于1的小数
-
split(要被分隔的串,存放分隔后的结果,分隔符):分隔字符串。
# netstat -tnl | awk '/^tcp\>/{split($4,ip,":"); print ip[1]}' 0.0.0.0 192.168.122.1 0.0.0.0 127.0.0.1 127.0.0.1
2,也支持自定义函数
c/c++ 学习互助qq群:877684253
本人微信:xiaoshitou5854