Awk介绍
报告生成器,格式化文本输出
gawk:模式扫描和处理语言
基本用法:
Awk [option] ‘program’ var=value file
Awk [ option] -F programfile var =value file F指明分隔符
Awk [option ] ‘BEGIN’{ACTION…} pattern{action;…} END{action;,,,}’ file,,,
Begin,没开始读,就开始做什么事,做表头
End,读完所有的行,干的事,统计平均成绩
工作原理:一行一行处理,自动读下一行,按定义的分隔符切割成字段,字段名,$1,$2,$3,,,对字段,轮流处理
基本格式
Awk [option] ‘program’ file
选项 awk自己的程序 要处理的文件
Program: pattern{action statements;….} 满足pattern就执行action,不满足就跳到下一行
模式 动作指令
Pattern部分决定动作语句合适触发及触发事件
Begin end
Action statements 对数据进行处理,放在{}内指明
Print打印必要的数据 printf详细的定义显示的格式,空格数,小数位
分隔符、域和记录
Awk执行时,由分隔符分隔的字段(域)标记$1,$2,,,,$n 称为域标识、$0为所有域,
文件的每一行称为记录,
省略action,则默认执行 print $0 的操作
Awk的工作原理
第一步:执行begin{action}语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern
{action,,}语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕
第三步:当读至输入流末尾时,执行END{action}语句块
Begin语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在begin语句块中
End语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在end语句块中完成,它也是一个可选语句块
Pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{print}即打印每一个读取到的行,awk读取的每一行都会执行该语句块
Print格式:print item1,item2,,,,
要点:1逗号分隔符
2输出的各item可以是字符串,也可以是数值;当前记录的字段、变量或awk的表达式
3如省略item 相当于print$0
实例: awk -F: ‘{print $3}’ /etc/passwd 以:为分隔符 取第三列
df |grep /dev/sd |awk ‘{print $5}’ 以默认空白为分隔符,取第五列
Field,域,字段,列,column,属性
行,记录,record
Awk:内置和自定义变量
FS:输入字符分隔符,默认为空白字符
FS内置变量,使用时要写-v
Awk -v FS=’:’ ‘{print $1,FS,$3}’ /etc/passwd
Awk -F: ‘{print $1,$3,$7}’ /etc/passwd
OFS:输出字段分隔符,默认为空白字符
输出的时候,定义为+为分隔符,OFS需要写-v,显示
Awk -v FS=’:’ -v OFS ’:’ ‘{print $1,$3,$7}’ /etc/passwd
RS:输入记录分隔符,指定输入时的换行符
输入记录换行符,默认为回车,
右图自定义为;碰见一个;就为一行
ORS:输出记录分隔符,输出时用指定符号代替换行符
定义—-为输出记录分隔符,即输出时—分隔一行
NF:字段数量 (NF本身就是一个变量,表示字段的数量) $NF 最后一个字段 $(NF-1)倒着数第一列
Ss -nt ss命令用来显示处于活动状态的套接字信息
df命令参数功能:检查文件系统的磁盘空间占用情况
$(NF-1) 倒数第二个字段
Httpd的访问日志,取$1
NR:记录号
Awk ‘{print NR}’ /etc/fstab
FNR:各文件分别计数,记录号
FILENAME:当前文件名
打印行号,空格也算一行,
FNR, 各文件独立编号 FILENAME:当前文件名
加文件名,各文件独立显示
ARGC:命令行参数的个数
ARGV: 数组,保存的是命令行所给定的各参数
命令行三个参数,’{}’是awk的程序
自定义变量(区分字符大小写)
可以在外面定义,也可以在里面定义
- -v var =value
- 在program中直接定义
-f 可以调用脚本
格式化输出Printf命令: “FORMAT”, item1,item2,,,
- 必须指定FORMAT
- 不会自动换行,需要显示给出换行控制符,\n
- FORMAT中需要分别为后面每个item指定格式符
格式符:与item一一对应
%c:显示字符的ASCII码
%d:%i:显示十进制整数
%e:%E:显示科学计数法数值
%显示为浮点数
%g,%G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%:显示%自身
修饰符:#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
– :左对齐(默认右对齐) %-15s
+ :显示数值的正负符号%+d
“”双引号里面的表示格式,%S $1按照字符串格式显示出来,默认不换行
%d $3 按照数字的格式显示出来 \n 表示换行
%-30s 表示的是字符左对齐,控制显示30字符,
%6d 表示的是数字右对齐,控制数字显示6字符
BEGIN在执行之前, 只是执行一次, 小数后面的位数溢出,自动四舍五入
例:
当读入一行,由于pattern没有定义,所以这个行就符合条件,符合条件,就执行action,就是打印,即print,hello.awk 执行结果和etc/fstab无关,只是这个文件有几行,就打印几次hello.awk
可以管道,cat /etc/fstab | awk ‘{print “hello,awk”}’
可以重定向,awk ’{print “hello,awk”}’ < /etc/fstab
支持/n换行 awk ‘{print “hello,awk\nhello.27class”}’ /etc/fstab
支持运算 awk ‘{print 2*3}’ /etc/fstab
可以用cut -d: -f3 /etc/passwd
awk -F: ‘{print $1,$3,$7}’ /app/passwd -F:分隔符为:没有写pattern,就是每一条都符合,按:来取,$3,就是取第三行uid,
Awk 默认的分隔符为 空白符
取分区利用率,$5第五行 ,
$1 $5 之间用逗号隔开,输出为空格,也可以在括号内用“ ”使用空格,$1时变量不能使用“”,“”表示使用的是字符串 “\t”“\n”也可以
$0表示打印整行,什么也不写默认打印$0
操作符
算数操作符
X+y,x-y,x*y, x/y,x^y, x%y
-x:转换为负数
+y:转换为正数
字符操作符:没有符号的操作符,字符串连接
赋值操作符:
=,+=,-=,*=,/=,%=,^=,
++,–
[root@Centos6 ~]# awk ‘BEGIN{i=0;print ++i,i}’ 先做运算,在打印出来
1 1
[root@Centos6 ~]# awk ‘BEGIN{i=0;print i++,i}’先赋值,在进行加运算
0 1
比较操作符
==, != >, >=, < <=
模式匹配符:
~:左边是否和右边匹配包含 !~:是否不匹配
实例
[root@Centos6 ~]# awk ‘$0 ~ “^wang”‘ /etc/passwd
wang:x:500:500::/home/wang:/bin/bash
[root@Centos6 ~]# awk ‘$0 ~ “^root”‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash
表示以wang开头的,^表示行首,,,省略{print}表示打印全行
[root@Centos6 ~]# awk ‘$0 ~ /^root/’ /etc/passwd
root:x:0:0:root:/root:/bin/bash
“” / / 都可以表示中间的是正则表达式,效果一样
[root@Centos6 ~]# awk ‘$0 ~ /^(root|wang)/’ /etc/passwd
root:x:0:0:root:/root:/bin/bash
wang:x:500:500::/home/wang:/bin/bash
支持扩展的正则表达式,使用””是一样可以使用扩展正则表达式
[root@Centos6 ~]# awk -F: ‘$3==0’ /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@Centos6 ~]# awk -F: ‘$3>=1000’ /etc/passwd
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
模式匹配,相当于”$3==0{print$0}”
变量的值,0,空字符,没定义,都当假 (在awk中0表示假,1表示真)
只要是有值,都认为为真
awk -v i=0 ‘i’ /etc/issue 相当于awk -v i=0 ‘i{print $0}’
awk -v i=” “ ‘i’ /etc/issue
awk -v i=”” ‘i’ /etc/issue
逻辑操作符:与&&,或||,非!
示例
[root@Centos6 ~]# awk -F: ‘ $3 >=0 && $3<=1000 {print $1}’ /etc/passwd
[root@Centos6 ~]# awk -F: ‘ $NF ==”/bin/bash” {print $1,$NF}’ /etc/passwd
root /bin/bash
wang /bin/bash
haha /bin/bash
hehe /bin/bash
[root@Centos6 ~]# awk -F: ‘!($3==0){print $1}’ /etc/passwd
匹配不是root的其他账号
!i++ 先做取反,在赋值计算,++i 先赋值计算,在取反
条件表达式(三目表达式)
—?—:—
awk -F: ‘{$3>=1000?usertype=”common user”:usertype=”sysuser”;printf “%-30s:%-30s %15d \n”,usertype,$1,$3″}’ /etc/passwd
PATTERN:根据pattern条件,过滤匹配的行,在做处理
- 如果未指定:空模式,匹配每一行
- /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
- Relational expression:关系表达式,结果为真,才会被处理
- 真:结果为非0值,非空字符串
假:结果为空字符串或0值
[root@Centos6 ~]# awk -F: ‘/^[^#]/{print $1}’ /etc/profile
[root@Centos6 ~]grep ‘^[^#]’ /etc/profile
awk -F: ‘!/^UUID/{print $1}’ /etc/fstab
awk -F: ‘/^UUID/{print $1}’ /etc/fstab
显示,非#开头的行
Seq 10
Seq 10 |sed -n ‘1-2p’ 打印奇数行
Seq 10 |sed -n ‘2-2p’ 打印偶数行
[root@Centos6 ~]# seq 10 |awk ‘!(i=!i)’
2
4
6
8
10
[root@Centos6 ~]# seq 10 |awk -v i=2 ‘(i=!i)’ 开始i=2,!i=0,不打印,第二次,i=0;!i=1,打印该行
2
4
6
8
10
[root@Centos6 ~]# seq 10 |awk ‘i=!i’ 开始i为空,!i=1,则打印改行,第二次,i=1;!i=0,则不打印该行
1
3
5
7
9
- line ranges 行范围
Startline,endline:/part1/,/part2/不支持直接给出数字格式
打印第二行到第五行
BEGIN和END模式
BEGIN{}:仅在开始处理文件中的文本之前执行一次
END{}:仅在文本处理完成之后执行一次
常用的action分类
1,算数,比较表达式
2,if,while 语句
3,组合语句
4,input statements
5,output statements:print等
Awk控制语句
{statements;…}组合语句
If(condition){statements,…}
If(condition){station;…}else{stations;…}
While(conditon){stations;…}
Do{stations;…}while(condition)
For(expr1;expr2;expr3){stations;…}
Break
Continue
Delete array[index]
Delete array
Exit
[root@Centos6 ~]# df |awk ‘{if($0 ~ /\/dev\/sd/)print $1,$5}’
/dev/sda3 8%
/dev/sda2 1%
/dev/sda1 4%
[root@Centos6 ~]# df |awk ‘/^\/dev\/sd/{print $1,$5}’
/dev/sda3 8%
/dev/sda2 1%
/dev/sda1 4%
如果分区的利用率大于80,就把分区的名称显示出来
[root@Centos6 ~]# df | awk -F% ‘/^\/dev\/sd/{print $(NF-1)}’
/dev/sda3 50264772 3582396 44122376 8
/dev/sda2 60344404 57644 57214760 1
/dev/sda1 999320 34952 911940 4
[root@Centos6 ~]# df | awk -F% ‘/^\/dev\/sd/{print $(NF-1)}’|awk ‘{if($5>4)print $1,$5}’
/dev/sda3 8
[root@Centos6 ~]# df | awk -F “[ %]+” ‘/^\/dev\/sd/{if($(NF-2)>4)print $1,$5}’
/dev/sda3 8
/dev/sda2 1
/dev/sda1 4
[root@Centos6 ~]# df | awk -F “[ %]*” ‘/^\/dev\/sd/{if($(NF-2)>4)print $1,$5}’
/dev/sda3 8
/dev/sda2 1
/dev/sda1 4
表示以空格和%同时为分隔符
[root@Centos6 ~]# awk -F: ‘{print $1,length($1)}’ /etc/passwd
root 4
bin 3
daemon 6
统计字符串的长度
[root@Centos6 ~]# awk -F: ‘/^root/{i=1;while(i<=NF){print $i,length($i);i++}}’ /etc/passwd
root 4
x 1
0 1
0 1
root 4
/root 5
/bin/bash 9
统计root行,各字符串的长度
[root@Centos6 ~]# awk -F: ‘BEGIN{i=1;sum=0;while(i<=100){sum+=i;i++}print sum}’
5050
[root@Centos6 ~]# awk -F: ‘BEGIN{i=1;sum=0;do{sum+=i;i++}while(i<=100);print sum}’
5050
[root@Centos6 ~]# seq -s+ 1 100
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
[root@Centos6 ~]# seq -s+ 1 100|bc
5050
While循环
语法:while(condition){statements;…}
条件“真”,进入循环;条件“假”,退出循环
使用场景:
对一行内的多个字段逐一类处理时使用
对数组中的各元素逐一处理时使用
示例
Do-while循环
语法:do{statement;…}while(condition)
意义:无论真假,至少执行一次循环体
For循环
语法:for(expr1;expr2;expr3){statements;….}
常见用法
特殊用法:能够遍历数组中的元素
语法for(var in array){for-body}
性能比较
time for((sum=0,i=1;i<=100;i++));do let sum+=i;done;echo $sum
[root@Centos6 ~]# time(total=0;for i in {1..10000};do total=$(($total+i));done;echo $total)
[root@Centos6 ~]# time(for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
[root@Centos6 ~]# time(sep -s “+” 10000|bc)
Switch语句
语法:switch(expression){case VALUE1 or /regexp/:statement1;case VALUE2 or /regexp2/:statement2;…default:statement}
Break和continue
[root@Centos6 ~]# awk -F: ‘BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2!=0)continue;sum+=i}print sum}’
2550
[root@Centos6 ~]# awk -F: ‘BEGIN{sum=0;for(i=1;i<=100;i++){if(i==10)break;sum+=i}print sum}’
45
Next:
提前结束对本行处理而直接进入下一行处理(awk自身循环)
[root@Centos6 ~]# awk -F: ‘{if($3%2!=0)next; print $1,$3}’ /etc/passwd
root 0
daemon 2
lp 4
只执行偶数行的循环,满足条件即不是偶数行next,跳过 不执行
Awk数组
关联数组:array[index-expression]
Index-expression
1,可使用任意字符串;字符串要是用双括号括起来
2,如果某数组元素实现不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历
示例
f1 aa bbb ccc aaaa bbbb ccc aa bbb
awk ‘!arr[$0]++’ f1
开始aa读进来,[“aa”]没有定义 arr[“aa”]没有值,!arr[$0]就为真,执行print;经过这次之后进行++,arr[aa]=1
同样的道理,arr[“bbb”]没有赋值,取反之后,!arr[“bbb”]为真,进行打印,打印之后进行++,arr[bbb]=1
………第二次遇到ccc,就不打印了,,,执行结果就是去掉重复行
Sort f1|uniq sort先排序,排序后
加加和赋值没有关系,先赋值,在进行加加
第一行读进来,没有定义,
Arr[aa]为空,取反!arr[aa]的结果为1,
再进行加加,加加之后,数组arr[“aa”]=1
第二次,碰到aa,arr[“aa”]=1,!arr[“aa”]=0
再进行加加,数组arr[“aa”]=2
Awk ‘arr[$0]++;’ dupfile
把一个文件中的多余行去掉
Awk数组
若要遍历数组中的每一个元素,要使用for循环
For(var in array){for-body}
注意:var会遍历array中的每一个索引
Netstat -nat 显示网路连接状态
[root@centos7 ~]# netstat -nat
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 52 192.168.27.8:22 192.168.27.1:51066 ESTABLISHED
tcp6 0 0 :::111 :::* LISTEN
[root@centos7 ~]# netstat -tan |awk ‘/^tcp/{state[$NF]++ }END{for(i in state){print i,state[i]}}’
LISTEN 9
ESTABLISHED 1
一行一行处理,
过滤以tcp开头的,数组的下标为$NF,最后一个变量的值当成下标,每读出来一个值,就加一次
统计完之后,用end,for循环,把状态遍历出来
前面一个{}把各状态次数,相加起来,统计的结果就是放在state[]当中
处理日志
统计每个ip访问了多少次
当访问次数超过1000次放到防火墙中
–j<目标>:指定要跳转的目标;
A:向规则链中添加条目;
-s:指定要匹配的数据包源ip地址;
INPUT链:处理输入数据包
并发连接数(正在连接的数目)达到一个数,就把他放到防火墙里,
[root@centos7 ~]# ss -nt|awk -F “[ :]+” ‘/ESTAB/{print $(NF-2)}’|sort |uniq -c
1 192.168.27.1
[root@centos7 ~]# ss -nt|awk -F “[ :]+” ‘/ESTAB/{ip[$(NF-2)]++}END{for(i in ip){print i,ip[i]}}’
192.168.27.1 1
并发连接数前三个面试题
Linux sort命令用于将文本文件内容加以排序
-r 以相反的顺序来排序
-n 依照数值的大小排序。
Linux uniq命令用于检查及删除文本文件中重复出现的行列
-c或–count 在每列旁边显示该行重复出现的次数
数值处理
字符串处理
Rand()返回0和1之间的一个随机数
awk ‘BEGIN{srand();for(i=0;i<10;i++)print int(rand()*100) }’
Length([s])返回指定字符串的长度
Sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为s
[root@centos7 ~]# echo “2008:08:08 08:08:08″| awk ‘sub(/:/,”-“,$1)’
2008-08:08 08:08:08
Sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将全部匹配的内容替换为s
[root@centos7 ~]# echo “2008:08:08 08:08:08″| awk ‘gsub(/:/,”-“,$1)’
2008-08-08 08:08:08
Split(s,array,[t]);以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2
[root@centos7 ~]# netstat -tan |awk ‘/^tcp\>/{split($5,ip,”:”);count[ip[1]]++}END{for(i in count){print i,count[i]}}’
0.0.0.0 5
192.168.27.1 1
^行首牟定,\>词尾牟定(例如tcp6就不符合),$5远程主机的IP及端口号,用:做分隔符切割,第五列,放到ip数组中 Ip[1]中存放的是ip地址,count[ip[1]]++ 同一个ip地址放到数组count中,计算数量,结束之后对count做遍历,打印i,i为count的下标,即ip地址,而count[i]就是对应的ip的数量
[root@centos7 ~]# awk ‘{for(i=1;i<=NF;i++)word[$i]++}END{for(i in word)print i,word[i]}’ /etc/profile
统计每个单词的数量 每一行读进来之后,每个单词作为word的下标,下标一样加加,不一样形成新的数组
默认以空格作为分隔符, 最后,进行统计,
[root@centos7~]# awk ‘{if($3==”male”){mnum++;msum+=$2}else{fnum++;fsum+=$2}}
END{printf “male:%d %.2f\nfemale:%d %.2f”,mnum,msum/mnum,fnum,fsum/fnum}’ f1
male:2 95.00
female:2 92.00
[root@centos7 ~]# awk ‘{num[$3]++;sum[$3]+=$2}END{for(sex in num){print sex,num[sex],sum[sex]/num[sex]}}’ f1
female 2 92
male 2 95
[root@centos7 ~]# vim f1
mage 100 male
wang 90 male
zhang 85 female
mo 99 female
使用数组,num[$3]人的个数,使用$3作为它的下标,表现为,num[“male”] num[“female”]
num[$3]++表示为male和female人数总和
num[$3]+=$2 同一个性别的下标,把成绩往相同的下标里累加,最后统计就可以,遍历
Awk自定义函数
[root@centos7 ~]# awk -f f1.awk
3
[root@centos7 ~]# vim f1.awk
function max(v1,v2){
v1>v2?var=v1:var=v2
return var
}
BEGIN{a=3;b=2;print max(a,b)}
调用awk函数
调用awk脚本
Awk中调用shell中的命令
[root@centos7 ~]# awk ‘BEGIN{system(“hostname“)}’
centos7.magedu.com
[root@centos7 ~]# awk ‘BEGIN{system(“uname -r“)}’
3.10.0-693.el7.x86_64
[root@centos7 ~]# awk ‘BEGIN{name=”mage”;system(“echo “name)}’
mage echo后面有空格
连接数大于3的禁用
ss -nt |awk -F “[ :]+” ‘/^ESTAB/{IP[$(NF-2)]++}END{for(i in IP){if(IP[i] >3)system(“iptables -A INPUT -s “i” -j REJECT”)}}’
向awk脚本传递参数
格式:
Awkfile var=value var2=value2…inputfile
注意:在BEGIN过程中不可用、直到首行输入完成以后,变量才可用。可以通过-v参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/90785