一、简介
和 sed 命令类似,awk 命令也是逐行扫描文件(从第 1 行到最后一行),寻找含有目标文本的行,如果匹配成功,则会在该行上执行用户想要的操作;反之,则不对行做任何处理。
awk有自己的编程预言,以实现更强大的功能,例如判断、循环等。
awk基本格式如下:
awk [option] '[BEGIN{ commands }] pattern{ commands } [END{commands}]' file
其中BEGIN、pattern和END三个语句块统称为program。
commands则是awk自己的编程语言,称之为action。
awk工作原理
第一步:执行BEGIN{ commands }语句块中的语句;
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ commands }语句块,如果pattern匹配则在此行执行commands操作。从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{ commands }语句块。
BEGIN语句块
在awk开始从输入流中读取行 之前 被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
END语句块
在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
pattern语句块
pattern是最重要的部分,它也是可选的。用于筛选要执行awk操作的行,如果没有提供pattern,则所有行执行commands。
-
empty:空模式,匹配所有行;ACTION作用在所有行上
-
/正则表达式/:仅将ACTION应用于能够被Regular Expression所匹配到的行;
awk -F: '/^[ab]/{print $1,$3}' /etc/passwd
-
relational expression:关系表达式,即结果为“真”、“假”的表达式,或者其结果能类同于“真”或“假”的表达;一般来说,其结果为非0数值或非空字符串即可类同为“真”,否则,则类同为“假”;··
awk -F: '$3>=500{print $1,$3}' /etc/passwd awk -F: 'NR==3{print $1,$3}' /etc/passwd
-
line ranges:行范围,通过两个正则表达式,分别确定开始的行和结束的行
awk -F: '/^root/,/^adm/{print $1,$3}' /etc/passwd
注意
在pattern中可以使用awk编程语言(包括变量),上面例子中出现的>= 以及~会在后面介绍
option选项
-F fs fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:
-v var=value 赋值一个用户定义变量,将外部变量传递给awk
-f scripfile 从脚本文件中读取,即将program存为scriptfile。
二、awk编程语言
2.1 awk的内置变量
FS 输入信息的字段分隔符(列),默认为空白
RS 输入信息的行分隔符,默认为换行符
OFS 输出信息的字段分隔符(列),默认为空白字符
ORS 输出信息行分隔符,默的认为换行符
NF 当前行的字段的数量
NR 记录当前是第几行,从1开始
FNR 每个文件分别计数,显示行号
FILENAME 当前文件名
变量使用举例
[root@192-168-80-114 ~]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 223.5.5.5
[root@192-168-80-114 ~]# awk '{print NF}' /etc/resolv.conf
4
2
[root@192-168-80-114 ~]# awk 'BEGIN{OFS=":"}{print FILENAME,FNR}' /etc/resolv.conf /etc/fstab
/etc/resolv.conf:1
/etc/resolv.conf:2
/etc/fstab:1
/etc/fstab:2
/etc/fstab:3
/etc/fstab:4
/etc/fstab:5
2.2 awk的内置函数
2.3 awk的操作符
算术操作符
如:A+B A-B A*B A/B
[root@192-168-80-114 ~]# awk -v f1=5 -v f2=2 'BEGIN{f3=f1+f2;print f3}'
7
字符操作符
赋值操作符
如:= += /= %= *=
[root@192-168-80-114 ~]# awk -v f1=5 'BEGIN{f1*=5;print f1}'
25
比较操作符
如:> >= < <=
awk -F: '$3>=500{print $1,$3}' /etc/passwd
逻辑操作符
&& 与运算 || 或运算
模式匹配操作符
~ 是否能被右侧指定的模式所匹配;
!~ 是否不能被右侧指定的模式所匹配
条件表达式
selector?if-true-expression:if-ials-expreion
[root@192-168-80-114 ~]# awk -F":" '{$3>=1000?usertype="com_user":usertype="sys_user";print $1,usertype}' /etc/passwd
root sys_user
bin sys_user
rpcuser sys_user
nfsnobody com_user
函数调用
调用函数来进行数据的处理
2.4 awk的流程控制
if-else
语法:
if (condition) statement [ else statement ]
if (condition) {statements} [else {statements}]
使用场景:对awk取得的整行或行中的字段做条件判断;
awk -F: '{if ($3>=500) print $1,$3}' /etc/passwd
#打印出用户ID大于500(即普通用户)的用户名以及用户ID
awk '{if (NF>=6) print NF,$0}' /etc/rc.d/rc.sysinit
#打印出字段数大于6的整行,以及其中的最后个字段
awk -F: '{if ($3>=500) {print $1,"is a common user"} else {print $1,"is a sysadmin or sysuser"}}' /etc/passwd
#判定每行如果用户ID大于500则打印用户名并指出是普通用户,否则打印用户名并指出为系统用户
while循环
语法:
while (condition) statement
while (condition) {statements}
使用场景:通常用于在当前行的各字段间进行循环;
awk '{i=1;while(i<=NF){if (length($i)>=6) {print $i};i++}}' /etc/issue
#打印每行中字段长度大于等于6的打印出来
do-while循环
语法:do statement while (condition)
do {statements} while (condtion)
awk 'BEGIN{i=1;do {print "test"; i++} while (i<1)}'
意义:至少执行一次循环体
for循环
语法:for (expr1;expr2;expr3) statement
for (expr1;expr2;expr3) {statements}
awk '{for(i=1;i<=NF;i++){if(length($i)>=6) print $i}}' /etc/issue
swtich
语法:switch (expression) {case VALUE or /REGEXP/: statement; …; default: statementN}
break and continue
break [n]:退出当前循环,n是一个数字,用于指定退出几层循环;
continue:提前结束本轮循环而进入下一轮;
next
提前结束对本行文本的处理,而提前进入下一行的处理操作;
awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
#打印用户ID为奇数的行中第一字段和第三字段
2.4 awk的数组
数组类似于其他语言中字典,表现形式:weekdays[mon]=“Monday”。
如果某数组元素事先不存在,则在引用时,awk会自动创建此元素并将其值初始化为空串;
如果需要遍历数组,使用for (i in weekdays)
举例使用:
#整个文件统一统计:~]
awk '{for(i=1;i<=NF;i++) {count[$i]++}}END{for(j in count) {print j,count[j]}}' /root/count.txt
#每行单独统计:
awk '{for(i=1;i<=NF;i++) {count[$i]++};for(j in count) {print j,count[j]};delete count}' /root/count.txt
#练习:统计当前系统上所有tcp连接的各种状态的个数;
ss -tan | awk '!/^State/{state[$1]++}END{for(i in state) print i,state[i]}'
#练习:统计指定的web访问日志中各ip的资源访问次数:
awk '{ip[$1]++}END{for(i in ip) print i,ip[i]}' /var/log/httpd/access_log
2.5 格式化输出
awk的输出命令之一:print
用法:print item1, item2, …
item:只存在以下三种类型
字符串
用引号引用,打印字符串;
#print "hello","world" 只显示双引号内的字符串
awk '{print "hello","world" }' /tmp/fstab
#awk依据/tmp/fstab逐行执行'{print "hello","world" }';其中两个单词间逗号表示打印输出时使用字符串分隔符(默认是空格)隔开。
变量
引用变量:直接使用变量名,便会输出变量的值
awk 'BEGIN{f1=2;print f1}'
如果实现使用-v 为变量赋值数字,且在引用时加了$则会出现下列情况
数值
无须加引号,直接输出数字
intem中出现$2数值等是表现第2片段的内容,$0代表引用awk正读取行的整行内容。
要点:
- 各item之间需要使用逗号分隔;而输出时的分隔符为默认为空白字符;
- 输出的各item可以为字符串或数值、当前记录的字段($#例如:$2)、变量或awk的表达式;数值会被隐式转换为字符串进行输出;
- print后面的item省略时,相当于运行“print $0”,用于输出整行;
- 输出空白字符:print " "
2、awk的输出命令之二:printf
用法:printf FORMAT,item1,item2,…
要点:
(1) 必须提供FORMAT;
(2) 与print语句打印输出时不同,printf打印输出不会自动换行,需要显式指定换行符:\n
(3) FORMAT中需要分别为后面的每个item指定一个格式符,否则item则无法显示;
格式符:都以%开头,后跟单个字符;
-
%c: 显示字符的ASCII码;
-
%d, %i:显示为十进制整数;
-
%e, %E: 科学计数法显示数值;
-
%f:显示为浮点数;
-
%g, %G:以科学计数法或浮点数格式显示数值;
-
%s: 显示为字符串;
-
%u:显示无符号整数;
-
%%: 显示%符号自身;
格式符依次对应后面出现的item1 item2…
修饰符:用以修正格式符已达到特殊的格式要求
- #[.#]:([]表示精度有时可以忽略不写)
左边的#:用于指定显示对应item时的宽度; 右边的#: 显示精度; - +:显示数值符号
- -:左对齐(如果宽带大于item本来的宽度,默认是向右靠起)
#输出时字段的分隔符由printf中的格式决定
awk -F: '{printf "%20s:%-20d\n",$1,$3}' /etc/passwd
root:0
bin:1
daemon:2
adm:3
lp:4
sync:5
shutdown:6
halt:7
mail:8
operator:11