awk命令详解
awk命令格式和选项
语法形式
1 | awk [options] 'program' file… |
命令选项
-F fs fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:
-v var=value 赋值一个用户定义变量,将外部变量传递给awk
-f scripfile 从脚本文件中读取awk命令
-m[fr] val 对val值设置内在限制,-mf选项限制分配给val的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。
模式 pattern
模式可以是以下任意一个:
- /正则表达式/:使用通配符的扩展集。
- 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试。
- 模式匹配表达式:用运算符
~
(匹配)和!~
(不匹配)。 - BEGIN语句块、pattern语句块、END语句块:参见awk的工作原理
awk脚本基本结构
1 | awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file |
awk的工作原理
1 | awk 'BEGIN{ commands } pattern{ commands } END{ commands }' |
第一步:执行
BEGIN{ commands }
语句块中的语句;第二步:从文件或标准输入(stdin)读取一行,然后执行
pattern{ commands }
语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。第三步:当读至输入流末尾时,执行
1
END{ commands }
语句块。
BEGIN语句块 在awk开始从输入流中读取行 之前 被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
END语句块 在awk从输入流中读取完所有的行 之后 即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
pattern语句块 中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行
{ print }
,即打印每一个读取到的行,awk读取的每一行都会执行该语句块。示例
1 | echo -e "A line 1nA line 2" | awk 'BEGIN{ print "Start" } { print } END{ print "End" }' |
当使用不带参数的print
时,它就打印当前行,当print
的参数是以逗号进行分隔时,打印时则以空格作为定界符。在awk的print语句块中双引号是被当作拼接符使用,例如:
1 | echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3; }' |
双引号拼接使用:
1 | echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1"="var2"="var3; }' |
{ }类似一个循环体,会对文件中的每一行进行迭代,通常变量初始化语句(如:i=0)以及打印文件头部的语句放入BEGIN语句块中,将打印的结果等语句放在END语句块中。
awk内置变量(预定义变量)
FS:字段分隔符等同-F
1 | awk -v FS=':' '{print $1,FS,$3}’ /etc/passwd |
OFS:输出字段分隔符,默认为空白字符
1 | awk -v FS=‘:’ -v OFS=‘:’ '{print $1,$3,$7}’ /etc/passwd |
RS:输入记录分隔符,指定输入时的换行符
1 | awk -v RS=' ' ‘{print }’ /etc/passwd |
ORS:输出记录分隔符,输出时用指定符号代替换行符
1 | awk -v RS=' ' -v ORS='###'‘{print }’ /etc/passwd |
NF:字段数量
1 | awk -F:‘{print NF}’ /etc/fstab 引用变量时,变量前不需加$ |
NR:行数记录号
1 | awk ‘{print NR}’ /etc/fstab |
FNR:各文件分别计数,记录号
1 | awk '{print FNR}' /etc/fstab /etc/inittab |
FILENAME:当前文件名
1 | awk '{print FILENAME}’ /etc/fstab |
ARGC:命令行参数的个数
1 | awk '{print ARGC}’ /etc/fstab /etc/inittab |
ARGV:数组,保存的是命令行所给定的各参数
1 | awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab |
FILENAME当前⽂件名FILENAME变量的使⽤
1 | awk '{print FILENAME,FNR,$1}' /etc/fstab /root/awktest.txt |
自定义变量量(区分字符大小写)
(1) -v var=value
(2) 在program中直接定义
将外部变量值传递给awk
1 | awk -v test='hello gawk' '{print test}' /etc/fstab |
查找进程pid
1 | netstat -antup | grep 7770 | awk '{ print $NF NR}' | awk '{ print $1}' |
操作符
比较操作符:
==, !=, >, >=, <=
模式匹配符::左边是否和右边匹配,包含:是否不匹配 示例:
!
1 | awk -F: '$0 ~ /root/{print $1}‘ /etc/passwd |
逻辑操作符:
与&&,或||,非!
示例:
1 | • awk -F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd |
运算级优先级表
!级别越高越优先
级别越高越优先
awk高级输入输出
读取下一条记录
awk中next
语句使用:在循环逐行匹配,如果遇到next,就会跳过当前行,直接忽略下面语句。而进行下一行匹配。next语句一般用于多行合并:
1 | cat text.txt |
当记录行号除以2余1,就跳过当前行。下面的print NR,$0
也不会执行。下一行开始,程序有开始判断NR%2
值。这个时候记录行号是:2
,就会执行下面语句块:'print NR,$0'
分析发现需要将包含有“web”行进行跳过,然后需要将内容与下面行合并为一行:
1 | cat text.txt |
简单地读取一条记录
awk getline
用法:输出重定向需用到getline函数
。getline从标准输入、管道或者当前正在处理的文件之外的其他输入文件获得输入。它负责从输入获得下一行的内容,并给NF,NR和FNR等内建变量赋值。如果得到一条记录,getline函数返回1,如果到达文件的末尾就返回0,如果出现错误,例如打开文件失败,就返回-1。
getline语法:getline var,变量var包含了特定行的内容。
awk getline从整体上来说,用法说明:
当其左右无重定向符|或<时: getline作用于当前文件,读入当前文件的第一行给其后跟的变量
var
或$0
(无变量),应该注意到,由于awk在处理getline之前已经读入了一行,所以getline得到的返回结果是隔行的。当其左右有重定向符
|
或<
时:getline则作用于定向输入文件,由于该文件是刚打开,并没有被awk读入一行,只是getline读入,那么getline返回的是该文件的第一行,而不是隔行。
示例:
执行linux的date
命令,并通过管道输出给getline
,然后再把输出赋值给自定义变量out,并打印它:
1 | awk 'BEGIN{ "date" | getline out; print out }' test |
执行shell的date命令,并通过管道输出给getline,然后getline从管道中读取并将输入赋值给out,split函数把变量out转化成数组mon,然后打印数组mon的第二个元素:
1 | awk 'BEGIN{ "date" | getline out; split(out,mon); print mon[2] }' test |
命令ls的输出传递给geline作为输入,循环使getline从ls的输出中读取一行,并把它打印到屏幕。这里没有输入文件,因为BEGIN块在打开输入文件前执行,所以可以忽略输入文件。
1 | awk 'BEGIN{ while( "ls" | getline) print }' |
关闭文件
awk中允许在程序中关闭一个输入或输出文件,方法是使用awk的close语句。
1 | close("filename") |
filename可以是getline打开的文件,也可以是stdin,包含文件名的变量或者getline使用的确切命令。或一个输出文件,可以是stdout,包含文件名的变量或使用管道的确切命令。
输出到一个文件
awk中允许用如下方式将结果输出到一个文件:
1 | echo | awk '{printf("hello word!n") > "datafile"}' |
控制语句:
1 | {statements;...}:组合语句; |
数组的定义
数字做数组索引(下标):
1 | Array[1]="sun" |
字符串做数组索引(下标):
1 | Array["first"]="www" |
使用中print Array[1]
会打印出sun;使用print Array[2]
会打印出kai;使用print["birth"]
会得到1987。
读取数组的值
1 | { for(item in array) {print array[item]}; } #输出的顺序是随机的 |
数组相关函数
得到数组长度:
1 | awk 'BEGIN{info="it is a test";lens=split(info,tA," ");print length(tA),lens;}' |
length返回字符串以及数组长度,split进行分割字符串为数组,也会返回分割得到数组长度。
1 | awk 'BEGIN{info="it is a test";split(info,tA," ");print asort(tA);}' |
asort对数组进行排序,返回数组长度。
输出数组内容(无序,有序输出):
1 | awk 'BEGIN{info="it is a test";split(info,tA," ");for(k in tA){print k,tA[k];}}' |
for…in
输出,因为数组是关联数组,默认是无序的。所以通过for…in
得到是无序的数组。如果需要得到有序数组,需要通过下标获得。
1 | awk 'BEGIN{info="it is a test";tlen=split(info,tA," ");for(k=1;k<=tlen;k++){print k,tA[k];}}' |
注意:数组下标是从1开始,与C数组不一样。
内置函数
awk内置函数,主要分以下3种类似:算数函数、字符串函数、其它一般函数、时间函数。
算术函数
格式 | 描述 |
---|---|
atan2( y, x ) | 返回 y/x 的反正切。 |
cos( x ) | 返回 x 的余弦;x 是弧度。 |
sin( x ) | 返回 x 的正弦;x 是弧度。 |
exp( x ) | 返回 x 幂函数。 |
log( x ) | 返回 x 的自然对数。 |
sqrt( x ) | 返回 x 平方根。 |
int( x ) | 返回 x 的截断至整数的值。 |
rand( ) | 返回任意数字 n,其中 0 <= n < 1。 |
srand( [expr] ) | 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。 |
举例说明:
1 | awk 'BEGIN{OFMT="%.3f";fs=sin(1);fe=exp(10);fl=log(10);fi=int(3.1415);print fs,fe,fl,fi;}' |
OFMT 设置输出数据格式是保留3位小数。
获得随机数:
1 | awk 'BEGIN{srand();fr=int(100*rand());print fr;}' |
格式化字符串输出(printf使用)
格式化字符串格式:
其中格式化字符串包括两部分内容:一部分是正常字符,这些字符将按原样输出; 另一部分是格式化规定字符,以"%"
开始,后跟一个或几个规定字符,用来确定输出内容格式。
格式 | 描述 | 格式 | 描述 |
---|---|---|---|
%d | 十进制有符号整数 | %u | 十进制无符号整数 |
%f | 浮点数 | %s | 字符串 |
%c | 单个字符 | %p | 指针的值 |
%e | 指数形式的浮点数 | %x | %X 无符号以十六进制表示的整数 |
%o | 无符号以八进制表示的整数 | %g | 自动选择合适的表示法 |
1 | awk -F: ‘{printf "%s",$1}’ /etc/passwd |
练习实例
输出当前所有用户
1 | awk -F: '{printf "user:%d ^C\n",NR,$1}' /etc/passwd |
ssh连接数
1 | ss -nt|awk -F "[[:space:]]+|:" 'NR>1{print $(NF-2)}' |
uid大于1000用户
1 | awk -F: '$3>=1000{print $1,$3}' /etc/passwd |
本机ip地址
1 | 7 |
分区使用率
1 | df|awk -F"[[:space:]]+|%" 'NR!=1{print $1,$5}' |
可登录用户
1 | cat /etc/passwd |awk '!/nologin$/{print $0}' |
⽂件ip_list.txt如下格式,请提取“.magedu.com”前⾯的主机名部分 并写⼊到该⽂件中:
1 | 1 blog.magedu.com |
统计/etc/fstab⽂件中每个⽂件系统类型出现的次数?
1 | awk '/^[UUID\/]/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab |
统计/etc/fstab⽂件中每个单词出现的次数?
1 | awk '{for(i=1;i<=NF;i++){word[$i]++}}END{for(j in word){print j,word[j]}}' /etc/fstab |
提取出字符串Yd$C@M05MB%9Bdh7dq+YVixp3vpw中的所有数 字?
1 | echo "yd$C@M05MB%9&Bdh7dq+yVixp3vpw" | awk 'gsub(/[^[:digit:]]/," ",$0)' |
有⼀⽂件记录了1-100000之间的随机的整数共5000个,存储的格 式,100,50,35,89,。。。请取出其中最⼤和最⼩的整数?
1 | ]# (echo -e "$RANDOM\c";for((i=1;<100;i++));do echo -e “,$RANDOM\c”;done)i>num.txt |
解决DOS***⽣产案例:根据web⽇志或⽹络连接数,监控当某个ip 并发连接数或者短时间内pv达到100,即调⽤防⽕墙命令封掉对应的ip,控频率每隔5分钟;防⽕墙命令为iptables -A INPUT -s IP -j REJECT?
1 | vim deny_dos.sh |
将以下⽂件内容中FQDN取出并根据其进⾏计数从⾼到低排序:
1 | http://mail.magedu.com/index.html |
将以下⽂本以inode为标记,对inode相同的counts进⾏累加,并且统 计出 同一inode中,beginnumber的最小值和endnumber的最大值
1 | inode|beginnumber|endnumber|counts| |
#######################
1 | ⽂件datafile内容如下: |
上面的数据表中包含名字、电话号码和过去三个月里的捐款,下面分别用awk解答:
1 | 1)显示所有电话号码? |
⽂件名AccQryFree2016.log,存在⽬录/root/boss/log/下,内容:
1 | 12:00:00 service start query_value,exited with value 0; |
使⽤netstat和awk统计服务器出现tcp⽹络状态并按数量排序?
1 | netstat -tan |grep "^tcp\b" |awk '{print $5}' |sort | uniq -c| sort -nr |
实现查询⽂件file1⾥⾯空格开始的所在的⾏号?
1 | 答:grep -n -E '^[[:sapce:]] ' file1|awk -F: '{print $1}' |
使⽤awk命令,计算⼀个⽬录下⽂件⼤⼩的总和?
1 | 答:ll |awk 'BEGIN{sum=0}{sum=sum+$5}END{print sum}' |
使⽤awk统计当前主机的并发访问量?
1 | 答:netstat -tan | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}' 显示结果: |
使⽤netstat -an输出格式,请编写脚本,统计输出连接到本地主机 数最多的10个ip,并按连接数从多到少排序?
tcp 0 52 172.18.118.155:22 172.18.116.232:49916 ESTABLISHED
答:
1 |
|
echo “连接到本地主机最多的10个ip是:$top10ip”nginx的access.log⽇志如下,⽤shell实现,将状态码为200的请求 的ip访问排名前10个列出来:
1 | 172.18.116.232 - - [18/May/2018:00:20:29 -0400] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36" "-" |
统计apaceh的access.log中访问量最多的5个ip?
1 | 172.18.116.232 - - [21/May/2018:05:29:11 -0400] "GET /favicon.ico HTTP/1.1" 404 209 "http://172.18.118.155:8080/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36" |
awk实现打印奇数⾏和偶数⾏
1 | 偶数seq 10|awk '!(i=!i)' |
匹配以f开头的⾏开始,到r开头的⾏结束之间的所有⾏
1 | awk '/^f/,/^r/' /etc/passwd |
查找netstat -nt命令的结果中Foreign Address列的地址,统计每个地址链接的次数,如果⼤于2次, 显⽰ip
1 | netstat -nt |awk '/^tcp/{print $5}'|awk -F: '{print $1}'|sort |uniq -c|awk '$1>1{print $2}' 172.18.116.232 |