awk介绍

2022-01-27   


awk介绍

awk基本用法:

which awk
ll /usr/bin/awk
man awk
awk '{print "hello,awk"}'  #接受标准输入重定向
awk '{print "hello,awk"}' < myscript.awk

$1 $2 $3代表各域,$0整行,无action默认print $0

print后接字符串用""引起来,否则默认为变量

默认的字段分隔符是是一个或多个空白字符

vi employee-multiple-fs.txt
101,John Doe:CEO%10000
102,Jason Smith:IT Manager%5000
103,Raj Reddy:Sysadmin%4500
104,Anand Ram:Developer%4500
105,Jane Miller:Sales Manager%3000

使用正则表达式来指定多个字段分隔符
awk -v FS= 'BEGIN {FS="[,:%]"}{print $2,$3}END{print "hello,world"}' employee-multiple-fs.txt  field separator  
df | tail -n +2|tr -s ' ' |cut -d' ' -f5 |tr -d %  #cut取分区利用率,需要压缩空格
df | awk -F"[[:space:]]+|%" '{print $5}' |tail -n +2  #awk默认的字段分隔符是一个或多个空白字符
awk -F"|" '{print $4}' access_http.log-20220109 | sort | uniq -c | sort -nr | head -3 #取Nginx日志访问量最高的前三个host
ifconfig eth0 | sed -n '2p' | awk '{print $2}' #取ip地址
hostname -I| cat -A

awk变量:

内置变量:
FS:输入字段分隔符,默认为空白字符,功能相当于 -F
$ awk -v FS=':'  '{print $1,FS,$3}' /etc/passwd
OFS:输出字段分隔符,默认为空白字符
$ awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd|head -n1
RS:输入记录record分隔符,指定输入时的换行符
ORS:输出记录分隔符,输出时用指定符号代替换行符
$ awk -v ORS='###' '{print $0}' /etc/passwd
NF:字段数量
$ awk -F: '{print NF}' /etc/passwd
NR:记录的编号
$ ifconfig eth0 | awk 'NR==2{print $2}'
FNR:各文件分别计数,记录的编号
FILENAME:当前文件名
$ awk '{print FNR,FILENAME}' /etc/issue /etc/os-release
ARGC:命令行参数的个数
$ awk '{print ARGC}' /etc/issue /etc/redhat-release
ARGV:数组,保存的是命令行所给定的各参数,每一个参数:ARGV[0],......
$ awk 'BEGIN{print ARGV[0]}' /etc/issue /etc/redhat-release  

awk格式化:

awk -F: 'BEGIN{printf "--------------------------------\n%-20s|%10s|\n--------------------------------\n","username","uid"}{printf "%-20s|%10d|\n--------------------------------\n",$1,$3}' /etc/passwd  #表格输出用户名和uid信息

awk操作符:

算术操作符:x+y, x-y, x*y, x/y, x^y, x%y
赋值操作符:=, +=, -=, *=, /=, %=, ^=,++, --
比较操作符:==, !=, >, >=, <, <=
模式匹配符:~ 左边是否和右边匹配,包含关系   !~ 是否不匹配
逻辑操作符:与:&&,并且关系  或:||,或者关系  非:!,取反
三目表达式:selector?if-true-expression:if-false-expression

模式pattern:

PATTERN:根据pattern条件,过滤匹配的行,再做处理

如果未指定:空模式,匹配每一行

awk -F: '{print $1,$3}' /etc/passwd

/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来

df | awk '/^\/dev\/sd/'
ifconfig eth0 | awk '/netmask/{print $2}' #取ip地址

relational expression: 关系表达式,结果为“真”才会被处理 真:结果为非0值,非空字符串 假:结果为空字符串或0值

awk  -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd #获取以bash作为登录解释器的用户信息

line ranges:行范围 ,使用变量NR间接指定行号 , /pat1/,/pat2/ 正则表达式两个模式之间的所有行

seq 10 | awk 'NR>=3 && NR<=6'  #利用NR指定行范围
awk -F"|" '/^2022-01-08T09:00:00/,/^2022-01-08T10:00:00/{if($8>=500) print $7,$8}' access_http.log-20220109  #典型示例:取nginx日志9点到10点之间的url和状态码

awk条件判断:

位置:里面

单分支:if(condition){statement;…}[else statement]
多分支:if(condition1){statement1}else if(condition2){statement2}else if(condition3)
{statement3}...... else {statementN}
example:
$ awk -F: '{if($3>=1000) print $1,$3}' /etc/passwd  #提取uid大于1000的用户信息
$ df | awk -F"[[:space:]]+|%" '/^\/dev\/sd/{if($5>80)print $1,$5}'  #提取超过80%的sd盘文件系统使用率

awk循环:

使用场景:对一行内的多个字段进行处理或遍历数组元素

while (condition) {statement;…}
for(expr1;expr2;expr3) {statement;…}
for(var in array) {for-body}
$ awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) 
{if(length($i)>=10){print $i,length($i)}; i++}}' /etc/grub2.cfg  #获取grub启动配置文件大于10个字符的字段
$ time (awk 'BEGIN{ total=0;for(i=0;i<=1000000;i++){total+=i;};print total;}')  #性能比较
$ time (total=0;for i in {1..1000000};do total=$(($total+i));done;echo $total)   #性能比较
$ echo 'dsFUs34tg*fs5a%8ar%$#@' |awk -F "" ' {for(i=1;i<=NF;i++){if ($i ~ /[0-9]/){str=(str $i)}} print str}'  #循环获取字符串中的数字

awk数组:

array_name[index-expression]
如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历
for(var in array) {for-body} #for循环遍历数组中的每个元素,var会遍历array的每个索引
$ awk -F"|" '!host[$4]++{print $4}' access_http.log-20220109 #去重打印Nginx日志host
$ awk -F"|" '!host[$4]++{next}END{for(i in host)print host[i],i}' access.log | sort -nr #统计Nginx日志文件中访问的host及其访问次数
$ awk -F"|" '{host[$4]++}END{for(i in host)printf "%-10s  %s\n",host[i],i}' access_http.log-20220109 | sort -nr  #上一条的格式美化版

awk函数:

awk 的函数分为内置和自定义函数

length([s]):返回指定字符串的长度
sub(r,s,[t]):对t字符串搜索r表示模式匹配的内容,并将第一个匹配内容替换为s
gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容
split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第
一个索引值为1,第二个索引值为2,…
system('cmd') 调用shell命令

官方文档:https://www.gnu.org/software/gawk/manual/gawk.html#Functions

$ awk 'BEGIN{srand();print rand()}'  #生成0和1之间的一个随机数
$ echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
$ echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
$ netstat -tn | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){if(count[i]>=10){system("iptables -A INPUT -s "i" -j REJECT")}}}'  #连接数大于10拒绝掉该ip

awk脚本:

检查出某时段内访问nginx服务次数超过3次的客户端IP
$ cat check_nginx_log.awk
#!/usr/bin/awk -f
#BEGIN {
 #beg="2022-01-08T03:00";
 #beg=strftime("%Y-%m-%dT%H:%M",systime()-3600) ;
 #定义一个小时前的时间,并格式化日期格式
 #end="2022-01-08T04:00";
 #end=strftime( "%Y-%m-%dT%H:%M",systime()-60) ;
 #定义结束时间
 #print beg;
 #print end;
#}
$1 > beg && $1 < end {#定义取这个时间段内的日志
 count[$2]+=1;#利用ip当做数组下标,次数当做数组内容
}
END {
 for(i in count){#结束从数组取数据代表数组的下标,也就是ip
 if(count[i]>3) { #如果次数大于3次,做操作
 print count[i],i;
 #system("iptables -I INPUT -S”i”j DROP" )
 }
 }
}


$ chmod +x check_nginx_log.awk
$ ./check_nginx_log.awk  -F'[|", "]' beg="2022-01-08T03:00" end="2022-01-08T04:00" access_http.log-20220109 | sort -nr

Q.E.D.