【系统运维】文本处理工具awk命令使用教程

TangLu 未命名 2023-05-20 21842 1

一、awk命令介绍

awk是一个强大的文本处理工具,它可以对命令行中给定的内容或者指定文本中进行数据提取、报告生成等复杂操作。awk在处理文本时是逐行将内容读取到内存中,然后按照自定义的规则对文本内容进行处理和输出。这一点和sed工具很像,但是又有不同,awk主要用于对标准输出进行处理,sed更多是对文本内容进行处理


二、awk命令格式

#直接使用awk操作文件的写法
awk [options] 'BEGIN{}pattern{commands}END{}' filename

#将标准输出传递给awk的写法
cat filename | awk [options] 'BEGIN{}pattern{commands}END{}'

#BEGIN{}:正式处理数据之前先执行一次的命令
#commands:需要执行的具体操作,可以多个
#pattern:和sed的地址定界一样用于条件匹配
#END{}:正式处理数据之后执行一次的命令
#由于awk经常会用到$符号,为了防止$被当成变量被解析,尽量使用单引号来引用命令


二、awk命令常用选项(对应命令格式中的options)

· -F:在awk中默认的分隔符是空白符(包含空格和制表符),使用-F选项可以自定义分隔符,支持指定多个分隔符

awk -F':' '{print $1}' /etc/passwd  #指定分隔符为冒号
netstat -n | grep EST | awk -F '[ :]+' '{print $4}'  #指定分隔符为空格或冒号,支持正则


awkjingjiang2.png


· -f:从指定的配置文件中读取awk命令,较少使用

awk -f awk.conf  test.txt


· -v:由于系统中的变量无法直接给AWK使用,所以需要单独定义,每一个“-v”定义一个变量

awk -v age=10 '{print age}'  #定义一个age变量
name=tanglu
awk -v user_name="$name" 'BEGIN{print user_name}'  #传递系统变量给awk



三、awk常用操作命令(对应命令格式中的commands)

该部分也称为main代码块,指明awk读取每行文件时需要做的操作。常用操作为print或printf(后者可以进行格式化,比如左右对齐等),默认操作为{print $0},也就是输出当前行,也可以增加一些自定义文本内容进行输出。文本内容需要用到引号,否则会被当做变量。


· print(直接输出)

微信截图_20171030151955.png


· printf(格式化输出)

%s:将字段内容以字符串格式输出

%d:将字段内容以数值类型格式输出

%f:将字段内容以浮点类型格式输出

-:将字段内容使用左对齐输出,指定对齐方式的时候需要同时指定占位符的数量,如%-10s

+:将字段内容使用右对齐输出,也是默认输出方式,指定对齐方式的时候需要同时指定占位符的数量,如%+10s

PS:printf还可以使用更多格式进行输出,因为使用较少略过

#将第1、第7字段进行格式化输出,使用20个占位符,不足的用空格补齐
awk 'BEGIN{FS=":"}{printf "%20s %20s\n",$1,$7}' /etc/passwd  #printf不会自动换行,需要加\n
awk 'BEGIN{FS=":"}{printf "%-20s %-20s\n",$1,$7}' /etc/passwd  #printf不会自动换行,需要加\n

printf.jpg


四、pattern——用来做内容匹配,支持正则表达式

· ~ | !~:对指定内容做匹配或取反匹配

awk -F: '$5~/root/{print $1} /etc/passwd'  #第5个字段匹配root
awk '$0~/qq.com/{print $0}' a.txt          #找出包含qq.com的行


awk4.jpg


· //,//:范围匹配

awk  '/12:48:01/,/13:30:00/{print $0}'  /var/log/message #过滤出指定时间范围的日志


· &&:逻辑与,多个条件需要同时匹配


· ||:逻辑或

cat /var/log/message | awk '$3=="linuxe" || $3 == "tanglu"'


· ==:精准匹配,非模糊匹配,一个=号是赋值,所以需要两个=号。下面示例可以看到nologins虽然包含了nologin,但是最终结果并没有将其匹配出来

awk5.jpg


!=:将过滤结果进行反选后进行匹配

awk '$3!="linuxe"' /etc/passwd


>=:大于等于,当然也有<=、>、<等用于判断

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


二、awk的内置变量

· $0:当前行


· $n位置变量,如print $1就是显示第一列的内容,也可以同时显示多列

awkjingjiang3.png


· BEGIN{ }与END{}:分别是在开始处理文本之前和处理完文本以后,执行一次且只执行一次的命令

awk -F: 'BEGIN{print "username      uid\n-----------------"}{print $1,$3}'  /etc/passwd 
awk 'BEGIN{print "我在开始"}{print $0}' a.txt
awk 'END{print "我在结尾"}{print $0}' a.txt
awk 'BEGIN{print "我在开始"} {print $0} END{print "我在结尾"} a.txt


· NF:记录awk命令所处理的每一行数据有几列字段。比如一行文字被分隔符为7列,那么{print NF}出的结果会显示成7。由于在awk中引用内置变量无需加$符,如果书写成{print $NF},那么显示的结果就是具体第7列的值了,所以也可以使用这个变量取文本最后一列的值。进行一些运算后还可以取到指定的列,如{print $(NF-1)}为倒数第二列

awk -F: '$NF=="/bin/bash"{print $1}' /etc/passwd


· NR:打印出每一行的行号,通常用于取指定行内容

cat /etc/passwd | awk '{print NR,$0}'  #显示出每行的行号
cat /etc/passwd | awk 'NR>1 && NR<5{print $0}'
cat /etc/passwd | awk 'NR==3{print $0}'                    #取出第3行内容
cat /etc/passwd | head -n 5 | awk 'NR==1,NR==3{print $0}'  #取第一行到第三方


· FNR:作用同NR,不过在处理多个文件时NR会把多个文件当作一个文件进行行数展示,FNR则对单个文件做行数的显示

awkvar2.jpg


· FS:输入字段分隔符,默认为空白字符,作用和-F选项类似

awk -v FS=':' '{print $1}' /etc/passwd


· OFS:输出字段分隔符,默认为空白字符

awk -v OFS=':'  '{print $2,$3}'/etc/passwd


· FIELDWIDTHS:以宽度作为分隔符,适合处理一些有字段缺失内容的文本

echo "AABBBCCCCDDDDD" | awk 'BEGIN{FIELDWIDTHS="2 3 4 5"}{print $1;print $2;print $3;print $4}'  #第一个字段2个字符‘第二个字段3个字符...
awk 'BEGIN{FIELDWIDTHS="2 2:6 2:6 2:13"} test.txt  #第一个字段2个字符,第二个字段跳过2个字符然后取6个字符...


· FPAT:使用正则匹配字符作为字段,适合于字段中包含了分隔符的场景,比如用逗号分隔,但是有一个字段中的文本又包含了逗号


· RS、ORS:输入换行符与输出换行符,通常要写在BEGIN代码块中,否则第一行文本不会受到影响

cat 1.txt
Hadoop|Spark|Flume--Java|PHP|Shell--tanglu|zhangsan|lisi

awk 'BEGIN{RS="--"}{print $0}' 1.txt  #指定行分隔符为--,将把上面的文本分成三行
Hdoop|Spark|Flume
Java|PHP|Shell
tanglu|zhangsan|lisi

awk 'BEGIN{RS="--";FS="|"}{print $3}' 1.txt  #同时指定多个分隔符
Flume
Shell
lisi

awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $3}' 1.txt  #修改了输出分隔符
Flume&Shell&Lisi

awk 'BEGIN{ORS=","}{print $0}' /tmp/ip.txt  #将默认的回车换行替换为逗号,实现将多行内容整合到一行,并用逗号分割


三、awk常用函数

# lentsh(str)  计算括号内字符串长度
# tolower(str)  将括号内字符串转换为小写
# toupper(str)  将括号内字符串转换为大写
# match(str,RE)  返回正则表达式匹配到的字符串的位置
# sub(RE,Repstr,str)  替换查找到的第一个字符串
# gsub(RE,Repstr,str)  替换查找到的所有字符串


四、awk数据筛选示例

1、根据行号筛选

awk 'NR==2' a.txt  #筛选出第二行
awk 'NR>2' a.txt  #筛选出第二行和之后的行


2、根据正则筛选

awk '/qq.com/' a.txt  #筛选带有qq.com的行
awk '$0~/qq.com/{print $0}' a.txt  #等价上面的命令
awk '$0~!/qq.com/{print $0}' a.txt  #筛选不包含qq.com的行


3、根据字段进行筛选

awk '$5~/qq.com/{print $0}' a.txt  #筛选第五个字段中包含qq.com的行


4、多个条件组合进行筛选

awk '$3=="male" && $6~/^170/{print $0}' a.txt  #输出第三个字段为male且第6个字段以170开头的行


5、按照范围进行筛选

awk 'NR==2,NR==7' a.txt  #输出第2到7行内容,注意一个=号是赋值,需要2个等号
awk 'NR==2,$6~/^170/' a.txt


6、从ifconfig命令中筛选出除了lo网卡外的所有IPv4地址

ifconfig | awk '/inet / && !($2 ~ /^127/){print $2}'


7、使用awk if实现判断

#可以遵循写法:{ if(){} else if(){} else if(){} else{} }
awk -F: '{if($3>=10) {print $1,$3}}' /etc/passwd
awk -F: '{if ($3~/^linuxe){print $0}}' /tmp/1.txt
awk -F: '{if($3==0){print $1,"the user is root"} else if($3>=500){print $1,"is common user"}}' /etc/passwd


8、awk数组。AWK默认支持关联数组(即索引下标可以是单词而不局限于数字),数组的语法格式

array_name[index]=value
#array_name:数组名称
#index:索引元素
#value:元素索引对应的值


awk数组功能常用于对某个字段进行统计,比如统计日志文件中某IP访问了多少次、对netstat中各个状态进行统计等。要实现这种功能的话需要定义一个数组,然后把需要统计的字段作为数组的索引即可


· 统计netstat命令各个状态数量

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'


· 计算当前系统中普通用户和管理员的个数并显示出来,这里count和i都是定义的变量

awk  -F:  '{if($3==0){count++} else{i++}} END {print “管理员个数:”count ; print “普通用户数:”i}'  /etc/passwd


· 统计日志中访问量最高的页面

awk '/05\/Sep\/2018/{url[$7]++} END{for(i in url){print i,url[i]} }' access.log |sort -k2 -rn

· 统计TCP连接数。这里的$6是连接状态,比如LISTEN、ESTABLISHED等,把这个作为status变量的索引进行统计,然后最终输出

netstat -na | grep 80 | awk '{status[$6]++} END {for (i in status){print i,status[i]} }'


· 使用awk进行计算,可用于统计某进程消耗资源。比如服务器Nginx启动了8个进程,那么该命令会将第一个进程消耗的CPU资源与后面依次相加,最后输出结果,当变量遇到与数值相加时默认是0,所以最后输出的结果就是1+2+3+4...=的结果了

ps aux |grep nginx |awk '{a=a+$3}END{print a}'


· 统计访问日志中各个状态码的百分比

cat access.log | awk '{count[$9]++} END {for (status in count ){print status,count[status]/NR*100"%"}}'


· 统计Nginx日志中每个IP访问量

awk '{ipcount[$1]++} END{for(i in ipcount){print i,ipcount[i]}}' access.log
#将日志中$1字段作为索引元素赋值给数组ipcount,所以该数组内容是ipcount[192.168.100.1]、ipcount[192.168.100.2]这样的每个IP;
#由于每个索引元素的值默认是1,然后每个索引元素的值会根据awk处理的次数进行递增
#for循环遍历ipcount数组的索引并赋值给变量i,此时变量i就是每个ip
#ipcount[i]就是取数组每个索引的值,i已经定义为ip,所以最后得到的就是自增结束后的总值,实现统计


评论

精彩评论
2016-08-01 09:38:25

老司机准备发车,biubiubiu