AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。
之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。
AWK有多种版本:
GNU AWK 用户手册文档
https://www.gnu.org/software/gawk/manual/gawk.html
gawk:模式扫描和处理语言,可以实现下面功能
vim: 是将整个文件加载到内存中 再进行编辑, 受限你的内存
awk(语言): 读取一行处理一行,
在 Linux/UNIX 系统中,awk 是一个功能强大的编辑工具,逐行读取输入文本,默认以空格或tab键作为分隔符作为分隔,并按模式或者条件执行编辑命令。而awk比较倾向于将一行分成多个字段然后进行处理。AWK信息的读入也是逐行
指定的匹配模式进行查找,对符合条件的内容进行格式化输出或者过滤处理,可以在无交互
的情况下实现相当复杂的文本操作,被广泛应用于 Shell 脚本,完成各种自动化配置任务。
工作原理:
前面提到 sed 命令常用于一整行的处理,而 awk 比较倾向于将一行分成多个“字段”然后再进行处理,且默认情况下字段的分隔符为空格或 tab 键。awk 执行结果可以通过 print 的功能将字段数据打印显示。
格式:
awk [选项] 'program' var=value file…
说明:
program通常是被放在单引号中,并可以由三种部分组成
BEGIN语句块
模式匹配的通用语句块
END语句块
常见选项:
-F “分隔符” 指明输入时用到的字段分隔符,默认的分隔符是若干个连续空白符
-v var=value 变量赋值
Program格式:
pattern{action statements;..}
pattern{action statements;..} 执行步骤
第一步:执行BEGIN{action;… }语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,
从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{action;…}语句块
BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块
基本格式:
[root@localhost ~]#awk 'patterm{action}'
[root@localhost ~]#awk ''
#什么都不写 空没有效果
[root@localhost ~]#awk '{print}'
##把你输入的内容(回车后)再打印一遍
dd
dd
[root@localhost ~]#awk '{print "hello"}'
#字符串需要添加双引号,单引号已被使用
#输入任何数回车 或 直接回车 后打印 hello
1
hello
a
hello
awk 还支持重定向和管道符。
[root@localhost ~]#awk '{print "hello"}' < /etc/passwd
#/etc/passwd中有几行就输出几行hello
[root@localhost ~]#ls | awk '{print "hello"}'
#当前目录下有几个文件就输出几次 hello。
[root@localhost ~]#awk 'BEGIN {print "hello"}'
#BEGIN比较特殊值打一行 pattern
hello
#运算
[root@localhost ~]#awk 'BEGIN{print 100+200}'
300
#不加 BEGIN 需要 回车 后才显示计算结果。 加上可以之间输出计算结果。
[root@localhost ~]#awk -F: 'BEGIN {print "hello"} {print $1}' /etc/passwd |head -n3
#先处理BEGIN 中的式子 ,再以 : 为分隔符 ,打印第一列。
hello
root
bin
[root@localhost ~]#awk -F: 'END {print "hello"} {print $1}' /etc/passwd |head -n3
#最后处理 END 中的式子
root
bin
hello
##### BEGIN{}模式表示,在处理指定的文本前,需要先执行BEGIN模式中的指定动作; awk再处理指定的文
本,之后再执行END模式中的指定动作,END{}语句中,一般会放入打印结果等语句。
[root@localhost data]#awk 'BEGIN {x=0};/\/bin\/bash$/;{x++};END{print x}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
w:x:1000:1000:w:/home/w:/bin/bash
41
#先定义变量 x=0 ,然后查看第一行是否以 /bin/bash结尾 ,如果是打印出来 并 执行 x++(x+1),
不是直接执行x++ 后继续查找第二行直至最后一行,最后执行 print x(查找次数,相当于行数)
[root@localhost data]#awk 'BEGIN {x=0};/\/bin\/bash$/ {x++;print x,$0};END{print x}' /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 w:x:1000:1000:w:/home/w:/bin/bash
2
#先定义变量 x=0 ,然后查找出 以 /bin/bash结尾的行,找到了就执行 x++ 并打印 变量 $x
以及 当前处理的行的整行内容(有,号就以空格隔开)。最后打印变量 $x (行数)
[root@localhost ~]#awk '{print "root"}' /etc/passwd
#打印root passwd里有多少行就打印多少行 root
[root@localhost ~]#echo {a..b} |awk '{print $1}'
#连续的空白符也可以
a
#分区利用率
[root@localhost ~]#df|awk '{print $5}'
#默认空格为分隔符 且 压缩空格。
已用%
8%
0%
0%
1%
0%
4%
0%
1%
-F 指定分隔符
[root@localhost ~]#cat /etc/passwd|awk -F: '{print $1,$3}'
#指定冒号作为分隔符,打印第一列和第三列 ,加 , 号以空格隔开
[root@localhost ~]#cat /etc/passwd|awk -F: '{print $1":"$3}'
#用冒号分隔开
[root@localhost ~]#cat /etc/passwd|awk -F: '{print $1"\t"$3}'
# \t : TAB键隔开
# \n 回车
使用awk 取ip 地址
[root@localhost data]#ifconfig ens33|sed -n '2p'|awk '{print $2}'
192.168.80.7
[root@localhost data]#hostname -I
192.168.80.7 192.168.122.1
[root@localhost data]#hostname -I|awk '{print $1}'
192.168.80.7
[root@localhost data]#wc -l /etc/passwd
41 /etc/passwd
[root@localhost ~]#awk -F: '{print $0}' /etc/passwd
#$0代表当前处理的行的整行内容
[root@localhost ~]#awk -F: '{print $1}' /etc/passwd
#代表第一列
[root@localhost ~]#awk -F: '{print $1,$3}' /etc/passwd
#代表第一第三列
[root@localhost ~]#awk '/^root/{print}' /etc/passwd
#打印以 root 为开头的行
[root@localhost ~]#awk '/root$/{print}' /etc/passwd
#打印以 root 结尾的行
awk 选项 '模式{print }'
################# FS 与 OFS #################
################# FS #################
指定每行文本的字段分隔符
[root@localhost ~]#awk -v FS=':' '{print $1FS$3}' /etc/passwd
#此处FS 相当于于变量 -v 变量赋值 相当于 指定 : 为分隔符
#以 : 号为分隔符,输出 第一列 第三列并以 : 分割开
[root@localhost ~]#awk -F: '{print $1":"$3}' /etc/passwd
#相同效果
######### 支持变量 #########
shell中的变量也可以给FS
[root@localhost data]#fs=":" #定义变量
[root@localhost data]#awk -v FS=$fs '{print $1FS$3}' /etc/passwd
#相当于把变量$fs的值传给 FS
############ OFS ############
输出时的分隔符
[root@localhost ~]#awk -v FS=':' -v OFS='==' '{print $1,$3}' /etc/passwd
root==0
bin==1
daemon==2
adm==3
lp==4
sync==5
#指定 : 号为分隔符 输出 第一列 和 第三列 时用 == 隔开。
############ RS ############
行分隔符
######## RS #######
行分隔符
默认是已 /n (换行符)为一条记录的分隔符
不要动它
[root@localhost data]#echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost data]#echo $PATH | awk -v RS=':' '{print}'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/root/bin
[root@localhost data]#
###################### NR ######################
当前处理的行的行号
###################### NR ######################
当前处理的行的行号
[root@localhost ~]#awk '{print $1,NR}' /etc/passwd
##先打印 符合要求的第一列内容然后在后面打印当前行号
[root@localhost ~]#awk '{print NR,$1}' /etc/passwd
##先打印行号 再打印第一列内容
注:awk默认以空格为分隔符且压缩空格。
[root@localhost ~]#awk 'NR==2{print $1}' /etc/passwd
#只取第二行的第一个字段
[root@localhost ~]#awk 'NR==1,NR==3{print}' passwd
#打印出1到3 行
[root@localhost ~]#awk 'NR==1||NR==3{print}' passwd
#打印出1和3行
打印奇数 ,偶数行
[root@localhost ~]#awk '(NR%2)==0{print NR}' passwd
#打印出函数取余数为0行 打印偶数行
[root@localhost opt]#seq 100|awk '(NR%2)==0{print NR}'
[root@localhost ~]#awk '(NR%2)==1{print NR}' passwd
#打印出函数取余数为1的行 打印奇数行
[root@localhost opt]#seq 100|awk '(NR%2)==1{print NR}'
[root@localhost ~]#awk 'NR>=3 && NR<=6{print NR,$0}' /etc/passwd
#打印3到6行
[root@localhost ~]#seq 10|awk 'NR>5 && NR<10'
#取 行间
6
7
8
9
[root@localhost opt]#seq 10|awk 'NR>7 || NR<2'
1
8
9
10
#打印行大于7或小于2的行。
[root@localhost ~]#awk -F: '$3>=1000{print}' /etc/passwd
#注意分隔符 以:号为分隔符,输出 第三列大于等于1000的行
#打印出普通用户 第三列 大于1000 的行
################ FNR ################
处理多个内容区分行号
[root@localhost opt]#cat a.txt|wc -l
4
[root@localhost opt]#cat b.txt|wc -l
3
[root@localhost opt]#awk '{print NR}' a.txt b.txt
1
2
3
4
5
6
7
#如上,使用NR查看行数无法分别查看a.txt b.txt的行而是合在一起输出。
[root@localhost opt]#awk '{print FNR}' a.txt b.txt
1
2
3
4
1
2
3
#分别处理行号
################ FILENAME ############
显示处理的文件名
[root@localhost opt]#awk -F: 'NR==2{print FILENAME"\n"$0}' /etc/passwd
/etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
# $0 打印匹配的行 \n回车
[root@localhost ~]#awk -v test='hello' 'BEGIN{print test}'
hello
#-v 定义变量 test='hello' 然后再打印一行 变量内容。
[root@localhost opt]#awk -v test1=test2="hello" 'BEGIN{print test1,test2}'
test2=hello
#看样子一次只能定义一个变量
[root@localhost opt]#awk 'BEGIN{test1=test2="hello";print test1,test2}'
hello hello
#可以看到test1和test2都被定义了。
awk -v test='hello gawk' '{print test}' /etc/fstab
#文件内有多少行就输出多少行 hello gawk
awk -v test='hello gawk' 'BEGIN{print test}'
#打印一行 hello gawk
[root@localhost opt]#awk 'BEGIN{test="hello,gawk";print test}'
hello,gawk
#可以直接在{}内定义变量并打印一行 hello,gawk
[root@localhost opt]#awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd
root male
bin male 18
daemon male 18
#{}内从左到右执行命令,先打印匹配的第一列然后打印变量 male ,因为第一次age没有被定义,
所以第一行后面没有18。
[root@localhost opt]#awk '{printf $1}' a.txt
asdadsasdasdasd[root@localhost opt]#
#没有换行。
[root@localhost opt]#
[root@localhost opt]#awk '{print $1}' a.txt
asd
ads
asdasd
asd
%s | 显示字符串 |
%d , %i | 显示十进制数 |
%f | 显示为浮点数 |
%e , %E | 显示科学计数法数值 |
%c | 显示字符的ASCII码 |
%g , %G | 以科学计数法或浮点形式显示数值 |
%u | 无符号整数 |
%% | 显示%自身 |
awk -F: '{printf "%s",$1}' /etc/passwd
awk -F: '{printf "%s\n",$1}' /etc/passwd
awk -F: '{printf "%20s\n",$1}' /etc/passwd
awk -F: '{printf "%-20s\n",$1}' /etc/passwd
awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
awk -F: '{printf “Username: %sUID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %25sUID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %-25sUID:%d\n",$1,$3}' /etc/passwd
打印表格:
awk -F: 'BEGIN{printf "--------------------------------\n%-20s|%10s|\n--------------------------------\n","username","uid"}{printf "%-20s|%10d|\n--------------------------------\n",$1,$3}' /etc/passwd
awk '模式{处理动作}'
PATTERN:根据pattern条件,过滤匹配的行,再做处理
如果模式为空表示每一行都匹配成功,相当于没有额外条件
awk -F: '{print $1,$3}' /etc/passwd
/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
例子:
awk '/^UUID/{print $1}' /etc/fstab
#匹配fstab文件中 以UUID开头的行,并打印匹配的第一列。
不支持使用行号,但是可以使用变量NR 间接指定行号加上比较操作符 或者逻辑关系
算术操作符
x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值
比较操作符:
==, !=, >, >=, <, <=
#####逻辑
与:&&,并且关系
或:||,或者关系
非:!,取反
模式匹配符:
~ 左边是否和右边匹配,包含关系
!~ 是否不匹配
例子:
关于 i++ 与 ++i
[root@localhost opt]#awk 'BEGIN{i=0;print i++,i}'
0 1
#i++ 的意思是输出 i 之后再 +1
[root@localhost opt]#awk 'BEGIN{i=0;print ++i,i}'
1 1
#++i 的意思是先 i+1,再输出 i
关于 ~ 与 !~
[root@localhost opt]#awk -F: '$0 ~ /root/{print $1}' /etc/passwd
root
operator
rooter
#输出匹配内容,不需要手动添加 命令默认有。
[root@localhost opt]#awk -F: '/root/{print $1}' /etc/passwd
root
operator
rooter
[root@localhost opt]#awk -F: '$0 !~ /root/{print $1}' /etc/passwd|head -n3
bin
daemon
adm
#输出不匹配内容。
[root@localhost opt]#awk -F: '$0 ~ "^root"{print $1}' /etc/passwd
root
rooter
[root@localhost opt]#awk -F: '$0 ~ /^root/{print $1}' /etc/passwd
root
rooter
[root@localhost opt]#awk -F: '/^root/{print $1}' /etc/passwd
root
rooter
[root@localhost opt]#awk -F: '$1 ~ /root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
rooter:x:1003:1003::/home/rooter:/bin/bash
#第一列是否与右边匹配。并输出匹配的行。
[root@localhost opt]#awk '/10:00/,/11:00/{print $0}' pipei.log
关系表达式结果为“真”才会被处理
真:结果为非0值,非空字符串
假:结果为空字符串或0值
[root@localhost ~]#seq 5 |awk 1
1
2
3
4
5
[root@localhost ~]#seq 10 |awk 0
#无输出结果。
seq 10 |awk 'n++' 打印除了第一行
seq 10 |awk '!n++' 只打第一行
seq 10 |awk '!0' 打印非0值
seq 10 |awk 'i=!i' 奇数行
seq 10 |awk -v i=1 'i=!i' 偶数行
seq 10 |awk '!(i=!i)' 偶数行
awk 选项 '模式 {actions}'
条件判断写在 actions里
awk内 if 语句格式:
if(condition){statement;…}[else statement]
if(condition1){statement1}else if(condition2){statement2}else if(condition3){statement3}...... else {statementN}
condition1:条件
statement1:语句
if语句:awk的if语句也分为单分支、双分支和多分支
单分支为if(判断条件){执行语句}
双分支为if(判断条件){执行语句}else{执行语句}
多分支为if(判断条件){执行语句}else if(判断条件){执行语句}else if(判断条件){执行语句}else if(判断条件){执行语句}
例子:
[root@localhost ~]#awk -F: '{if($3>1000)print $1,$3}' /etc/passwd
nfsnobody 65534
mysql 1001
lisi 1002
liwu 1003
[root@localhost ~]#awk -F: '{if($3>1000){print $1,$3}else{print $3}}' /etc/passwd
#如果第三列大于1000 输出第一列和第三列。如果不大于 1000,输出第三列内容。
awk内for语句格式:
for(expr1;expr2;expr3) {statement;…}
for(variable assignment;condition;iteration process) {for-body}
for(var in array) {for-body}
例子:
在awk内使用for语句 打印1到100的和。
[root@localhost opt]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++){sum+=i};{print sum}}'
5050
[root@localhost ~]#awk 'BEGIN{i=0;print i++,i}'
0 1
[root@localhost ~]#awk 'BEGIN{i=0;print ++i,i}'
1 1
awk的运算:
[root@localhost ~]# awk 'BEGIN{x=10;print x}' //如果不用引号awk就当作一个变量来输出了,所以不需要加$了
10
[root@localhost ~]# awk 'BEGIN{x=10;print x+1}' //BEGIN在处理文件之前,所以后面不跟文件名也不影响
11
[root@localhost ~]# awk 'BEGIN{x=10;x++;print x}'
11
[root@localhost ~]# awk 'BEGIN{print x+1}' //不指定初始值,初始值就为0,如果是字符串,则默认为空
1
[root@localhost ~]# awk 'BEGIN{print 2.5+3.5}' //小数也可以运算
6
[root@localhost ~]# awk 'BEGIN{print 2-1}'
1
[root@localhost ~]# awk 'BEGIN{print 3*4}'
12
[root@localhost ~]# awk 'BEGIN{print 3**2}'
9
[root@localhost ~]# awk 'BEGIN{print 2^3}' //^和**都是幂运算
8
[root@localhost ~]# awk 'BEGIN{print 1/2}'
0.5
awk数组特性:
1. 在内部,awk数组的索引全都是字符串,即使是数值索引在使用时内部也会转换成字符串 2. awk的数组元素的顺序和元素插入时的顺序很可能是不相同的
数组名[索引下标]=数值
索引可以是整数、负数、0、小数、字符串。如果是数值索引,会按照CONVFMT变量指定的格式先转换成字符串
例子:
[root@localhost opt]#awk 'BEGIN{a[1]="zhangsan";print a[1]}'
zhangsan
[root@localhost opt]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
Monday
awk提供了 length() 函数来获取数组的元素个数,它也可以用于获取字符串的字符数量。还可以获取数值转换成字符串后的字符数量。
例子:
[root@localhost opt]#awk 'BEGIN{arr[1]=1;arr[2]=2;print length(arr);print length("hehe")}'
2
4
#length()中想要获取字符串数量要加 双引号。
#length()中字符串加单引号不行,数组可以但没必要。
[root@localhost opt]#awk 'BEGIN{a[1]="zhangsan";print a[1];print length(a)}'
# length(a) length(数组名) 可以查看数组长度
zhangsan
1
例子:
[root@localhost opt]#awk 'BEGIN{students[1]="zhaizong";students[2]="hezong";students[3]="haizong";for(x in students){print x":"students[x]}}'
1:zhaizong
2:hezong
3:haizong
将下标转换成 字母
a
aa 后顺序就会不确定
[root@localhost opt]#awk 'BEGIN{students["a"]="zhaizong";students["aa"]="hezong";students["aaa"]="haizong";for(x in students){print x":"students[x]}}'
aaa:haizong
a:zhaizong
aa:hezong
去重复行
[root@localhost opt]#cat test.txt
abc
adfw
wsw
adfw
abc
fesc
wsw
abc
方法1:
[root@localhost opt]#awk '{a[$0]++}END{for (i in a){print i,a[i]}}' test.txt
abc 3
fesc 1
adfw 2
wsw 2
#重复行被去掉了并且显示重复了多少行。
方法2:
[root@localhost opt]#awk 'a[$0]++' test.txt
adfw
abc
wsw
abc
[root@localhost opt]#awk '!a[$0]++' test.txt
abc
adfw
wsw
fesc
统计出当前网络连接状态(去重,统计行数)
方法一、
[root@localhost opt]#ss -nta|awk 'NR!=1{a[$1]++}END{for(i in a){print i,a[i]}}'
LISTEN 11
ESTAB 1
方法二、
[root@localhost opt]#ss -nta|awk 'NR!=1{print $1}'|sort|uniq -c
1 ESTAB
11 LISTEN
将awk程序写成脚本,直接调用或执行
[root@localhost opt]#vim passwd.awk
{if($3>=1000)print $1,$3}
[root@localhost opt]#awk -F: -f passwd.awk /etc/passwd
nfsnobody 65534
zhangsan 1000
mysql 1001
[root@localhost opt]#cat test.awk
#!/bin/awk -f
#声明解释器
#this is a awk script
{if($3>=1000)print $1,$3}
[root@localhost opt]#./test.awk -F: /etc/passwd
nfsnobody 65534
vv 1000
lisi 1001
zhangsan 1002
rooter 1003
面试题:
提取下面的字段中的 IP地址和时间
[root@localhost opt]#cat access_log
58.87.87.99 - - [09/Jun/2020:03:42:43 +0800] "POST /wp-cron.php?doing_wp_cron=1591645363.2316548824310302734375 HTTP/1.1" ""sendfileon
128.14.209.154 - - [09/Jun/2020:03:42:43 +0800] "GET / HTTP/1.1" ""sendfileon
64.90.40.100 - - [09/Jun/2020:03:43:11 +0800] "GET /wp-login.php HTTP/1.1"""sendfileo
提取出来的格式为:
64.90.40.100 09/Jun/2020:03:43:11
[root@localhost opt]#cat co.log | awk -F '[ []' '{print $1" "$5}'
58.87.87.99 09/Jun/2020:03:42:43
128.14.209.154 09/Jun/2020:03:42:43
64.90.40.100 09/Jun/2020:03:43:11
提取host.txt主机名后再放回host.txt文件 >>
1 www.kgc.com
2 mail.kgc.com
3 ftp.kgc.com
4 linux.kgc.com
5 blog.kgc.com
[root@localhost opt]#cat host.txt | awk -F '[ .]' '{print $2}'
www
mail
ftp
linux
blog
[root@localhost opt]#cat host.txt | awk -F '[ .]' '{print $2}' >> host.txt
[root@localhost opt]#cat host.txt
1 www.kgc.com
2 mail.kgc.com
3 ftp.kgc.com
4 linux.kgc.com
5 blog.kgc.com
www
mail
ftp
linux
blog
或者使用
cat host.txt|awk '{print $2}'|awk -F'.' '{print $1}' >> host.txt