awk命令
一、基础概念
awk命令是Linux文本处理三剑客之一,其man文档把功能概况为pattern scanning and processing language(模式扫描机处理语言),通俗来讲awk命令是一个文本报告生成器,用于实现格式化文本输出,在处理文本文件时对文档中的某字段按条件执行操作,作者为aho、weinberger、kernighan,命令名称由其名字的首字母组成。awk命令大概出现在1970年左右的unix系统中,后来在开源领域不得不把awk所有的功能在Linux中重新实现,现在Linux用到的awk其实是gawk命令
二、命令用法
命令格式:gawk [option] 'program' File…
1. option
-F
:指明输入字段时的分隔符-v var=value
:自定义变量,变量名区分大小写内建变量:
FS:input field separator,输入时的字段分隔符,默认为空白字符
OFS:out field separator,输出时字段的分隔符,默认为空白
RS:input record separator,输入时的换行符
ORS:output record separator,输出时的换行符
NF:number of field,每一行的字段数量
NR:number of record,文件中的行数
FNR:file number of record,对多个文件分别计数
FILENAME:显示文件名
ARGC:命令行中参数个数
ARGV[0]:命令行中所给定的各参数,需要指定下标[#]
2. program
program
由patter
(模式)及用一对花括号包括的ACTION STATEMENTS
(动作语句)组成
(1)PATTER
在awk里是类似地址定界,可在patter前加!表示取反之意
-
empty
:不定义patter,处理文本文件的每一行 -
BEGIN{}
:仅在开始处理文件中的文本之前执行一次 -
END{}
:仅在文本处理完成之后,命令之前执行一次
|
/regular expression/
:写明正则表达式,仅处理匹配行
显示fstab中以uuid开头的行:awk '/^UUID/{print}' /etc/fstab
relational expression
:关系表达式,结果为真的才被处理,非0为真,非空串为真
显示id号大于100的用户:awk -F: '$3>100{print $1,$3}' /etc/passwd
/pat/,/pat/
:指明两个关键字,匹配之间的内容
(2)ACTION STATEMENTS
动作语句,语句中用分号分割
① print命令
print item,item2...
:item之间用逗号隔开,item可以是字符串、数值、当前记录的字段、变量或awk表达式,省略item,相当于打印$0
② printf命令
printf "FORMAT",item1,item2
:完成格式化输出的命令,通过FORMAT设定格式,然后把每一个item对位放在FORMAT中的每个格式符的位置。FORMAT必须要给出,其输出信息不会自动换行,需要在FORMAT中显示给出换行控制符\n才能完成换行
|
- 示例一:显示
/etc/passwd
文件中的用户名称
|
给定printf命令,用一组双引号将FORMAT包含,FORMAT中使用格式符%s为后面的变量进行占位并控制格式,处理时把$1的值替换到%s处,最后在手动指定\n换行,FORMAT和item之间使用逗号分割
- 示例二:显示
/etc/passwd
文件中的用户名称和ID号
|
同示例一,只不过示例二给定两个格式符%s和%d,awk会自动将两个item对位替换至格式符处
- 示例三:显示
/etc/passwd
文件中的用户名称和ID号,加入修饰符
|
示例三使用格式符+修饰符%10s和%4d,对输出信息进行宽度控制,只不过对其方式为右对齐
- 示例四:显示
/etc/passwd
文件中的用户名称和ID号,加入修饰符并调整为左对齐
|
③ expressions:条件表达式
格式为:selector?if-true:if-false
|
- 示例一:判断用户的id是否大于1000,如果大于则显示user,否则显示sys
awk -F: '{$3>=1000?type="user":type="sys";printf "username: %-10s UID: %-5d type: %-7s\n",$1,$3,type}' /etc/passwd
|
通过对第三个字段的数值进行判断,如果大于1000则复制type变量为user,小于则赋值type变量为sys,变量赋值时一定要加上双引号
④ control(控制语句)
if语句用法
- 单分支:
if(condition) statements
,单分支语句不用加花括号
condition:条件
statements:条件为真的语句
|
|
|
- 双分支:
if(condition) {statements} else {statements}
else后跟条件为假的语句,注意加话括号
|
while循环
while循环格式:var;while(condition) {statements}
第一次判断时如果为假则一次都不循环,一般使用在对一行内的多个字段逐一进行处理时使用,也可对数组中的元素处理时。循环前先定义变量i
,用分号分割i
的修正语句
|
首先使用patter搜索包含search字符串的行,接着定义变量i,当i的数值小于本行的长度NF的数值时,打印当前i的内容及用内建函数length()统计的数值,最后做变量i数值修正
do while循环
格式:do {statements} while(condition)
不管条件为真为假,都会先执行一遍循环体
for循环
for:可以遍历数组元素。
格式:for(变量赋值;条件判断;变量修正) statements
|
|
示例二中嵌套了一个if语句,需要注意嵌套的if语句要包含在花括号内
- for循环的特殊用法:
for(var in array) {statements}
:此种方式会把数组array的下标赋值给变量var,接着就可以遍历数组中的元素了
switch语句
语法格式:switch (expression) {case value1 or /regexp/: statement; case value2 or /regexp/;default:statement}
循环控制控制符
break[#]
:退出n层循环continue
:提前结束这轮循环,进入下一轮next
:awk中的特殊语句,提前结束awk对本行的处理,而进入下一行exit
:退出
三、awk中的数组
关联数组格式:array[index]
1. index的类型
可以有以下几种常见的种类
- 普通索引:可使用任意字符串,字符串要加双引号
- 为声明索引:如果某索引元素实现不存在,在引用时awk会自动创建此元素,并将其初始为空,如果做数值计算,这个元素就会被当成0使用。需要注意如果想知道某个数组是否存在某元素,要使用index in array 格式进行
2. 定义数组
定义格式:array["index_expression"]=""
~]# awk 'BEGIN{day["1"]="monday";day["2"]="tuesday";print day["1"]}'
monday~]# awk 'BEGIN{day["1"]="monday";day["2"]="tuesday";print day["2"]}'
tuesday
3. 遍历数组
遍历数组中的元素需要使用for循环时,变量i会替换成数组的下标
|
|
|
四、awk中的函数
做数值处理
rand()
:返回0和1之间的随机小数
字符串处理
length([s])
:返回指定字符串的长度sub(r,s,[t])
:以r所表示的模式,查找t所表示的字符串,并将其第一次出现替换为s所表示的内容gsub(r,s,[t])
:以r所表示的模式,查找t所表示的字符串,并将其所有替换为s所表示的内容split(s,a[,r])
:以r为分割符切割字符s,并将其切割后的结果保存至a所表示的数组中,数组编号从1开始
五、练习
- 统计/etc/fstab文件中,每个文件系统的个数
awk '!/^#|^$/{filetype[$3]++}END{for(i in filetype)print i,filetype[i]}' /etc/fstab
awk '/^UUID/{type[$3]++}END{for(i in type) print i,type[i]}' /tmp/fstab
- 统计指定文件中,每个单词出现的个数;
awk '{for(i=1;i<=NF;i++) word[$i]++}END{for(i in word) print i,word[i]}' /etc/fstab
- 如果用户的id号大于1000,就输出系统用户,小于则显示普通用户
awk -F: '{if($3>1000){printf "Username: %-15s Common User\n",$1} else {printf "Username: %-15s Admin user\n",$1}}' /etc/passwd
- 如果用户shell为/bin/bash,则显示用户名与bash
awk -F: '$NF=="/bin/bash"{printf "Username: %-15s %10s\n",$1,$7}' /etc/passwd
awk -F: '{if($NF~"/bin/bash") {printf "Username: %-15s %10s\n",$1,$NF}}' /etc/passwd
- 如果某一行字段大于5个,就显示整个字段,否则不显示
awk -F: '{if(NF>5) {print}}' /etc/passwd
- df -h显示的设备空间空间大于10则显示
df -h | awk -F% '{print $1}' | awk 'BEGIN{print "===========devuse==========="}/^\//{if($NF>10) {printf "devname: %-5s use: %d%%\n",$1,$NF }}'
- 打印每个字段以及字段的长度
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i);i++}}' /etc/grub2.cfg
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
- 打印每个字段以及字段的长度,仅显示大于等于7的
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}i++}}' /etc/grub2.cfg
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {if(length($i)>=7) {print $i,length($i)}}}' /etc/grub2.cfg
- 显示用户id号为偶数的用户
awk -F: '{if($3%2!=0)next;print $1,$3}' /etc/passwd
- ss查看状态,取出各状态名以及出现次数
ss -tan | awk '!/^State/{sstype[$1]++}END{for(i in sstype){print i,sstype[i]}}'
- 查看/var/log/httpd/access_log,每个ip对服务器请求
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log