一.AWK基本概念
1.基本用法
1.Centos中的文本处理工具
1)grep,egrep,fgrep:基于pattern(模式)的文本过滤工具
2)sed:流编辑器,行编辑器;通过模式空间,保持空间实现编辑
3)AWK:报告生成器,实现格式化文本输出
gawk - pattern scanning and processing language:基于模式扫描以及语言处理
1.AWK: Aho, Weinberger, Kernighan --> New AWK, NAWK
2.现如今一般使用的是GNU awk,即为gawk
awk实际为gawk的符号链接
[root@sakura ~]# which awk
/usr/bin/awk
[root@sakura ~]# ls -l /usr/bin/awk
"lrwxrwxrwx. 1 root root 4 5月 6 16:16 /usr/bin/awk -> gawk"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
ps:gwak是一种过程式编程语言。gwak还支持条件判断,数组,循环等各种编程语言中所有可以使用的功能,因此可以将其称为一种脚本语言解释器。
2.基本用法:gawk [options] 'program' FILE ...
1)program:pattern{action statements}
1.program:编程语言
2.pattern:模式
3.action statements:动作语句,可以由多个语句组成,各个语句间使用分号分隔;例如print;pringtf
- 1
- 2
- 3
2)-F选项:指明输入时用到的字段分隔符
3)-v var=value:自定义变量
4)awk在处理文本时也是一次读取一行,根据分隔符来进行切片,将分割出来的每一片都保存在awk的内部变量中,其分别为$1,$2,$3…以此类推至最后一个变量
3.print:print item1,item2,....;awk的输出命令之一
1)1)item:字符串,用引号引用,并且各item之间需要使用,隔开;例如print “kasumi”,“world”
[root@sakura ~]# awk -F: '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
若各item间未使用,隔开,则会将$1$3连续输出
[root@sakura ~]# awk -F: '{print $1 $3}' /etc/passwd
root0
bin1
[root@sakura ~]# awk -F: '{print $1$3}' /etc/passwd
root0
bin1
ps:其输出时默认的分隔符为空白字符
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2)输出的各item可以当字符串,也可以是数值;当前记录的字段,变量或awk的表达式;数值会被隐式转换为字符串进行输出
字符串 item为字符串时需要使用" ",若将$1写入" "中,则不会引用awk内部变量
[root@sakura ~]# awk -F: '{print "kasumi:",$1,$3}' /etc/passwd
kasumi: root 0
kasumi: bin 1
[root@sakura ~]# awk -F: '{print "kasumi:$1"}' /etc/passwd
kasumi:$1
数值 item为数值时不需要" "
[root@sakura ~]# awk -F: '{print 516,$1,$3}' /etc/passwd
516 root 0
516 bin 1
当前记录的字段
[root@sakura ~]# awk -F: '{print $1,$7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
变量 ietm为变量时不需要使用$
[root@sakura ~]# awk -v path=kasumi 'BEGIN{print path}'
kasumi
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
3)如果省略item,则相当于$0,即输出整行
[root@sakura ~]# awk -F: '{print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
- 1
- 2
- 3
4)输出空白字符:print “”
[root@sakura ~]# tail -2 /etc/passwd | awk '{print ""}'
ps:其表示为每处理一行信息,就输出空白字符
- 1
- 2
- 3
4.awk内建变量
1)FS:input field seperator,输入字段分隔符,与-F选项作用相同,默认为空白字符
[root@sakura ~]# awk -v FS=":" '{print $1,$3}' /etc/passwd
root 0
bin 1
- 1
- 2
- 3
2)OFS:output field seperator,输出时分隔字符,默认为空白字符
[root@sakura ~]# awk -v FS=":" -v OFS=":" '{print $1,$3}' /etc/passwd
root:0
bin:1
- 1
- 2
- 3
3)RS:input record seperator,输入时得换行符,默认为\n换行符
[root@sakura ~]# awk -v RS=":" '{print}' /etc/passwd
root
x
0
0
ps:文本本身自带的\n换行符也会启换行作用
- 1
- 2
- 3
- 4
- 5
- 6
4)ORS:utput record seperator,输出时的换行符,默认为\n换行符
[root@sakura ~]# awk -v RS=":" -v ORS="#" '{print}' /etc/passwd
root#x#0#0#root#/root#/bin/bash
bin#x#1#1#bin#/bin#/sbin/nologin
- 1
- 2
- 3
5)NF:number of field,字段数量
1.NF 每行按分隔符计量的字段数
[root@sakura ~]# awk -F" " '{print NF}' /etc/fstab
0
1
2
2.$NF 表示仅显示每行最后一个字段,NF会被替换为每行的字段数量
[root@sakura ~]# awk -F" " '{print $NF}' /etc/fstab
#
/etc/fstab
2019
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
6)NR:number of record,统计行数,即给每行显示行数;命令后跟的所有文件将统一合并计数
[root@sakura ~]# awk '{print NR}' /etc/fstab /etc/issue
1
2
3
ps:若命令后跟多个文件,其行数不会分别显示
- 1
- 2
- 3
- 4
- 5
7)FNR:各文件分别计数;行数
[root@sakura ~]# awk '{print FNR}' /etc/fstab /etc/issue
- 1
8)FILENAME:当前文件名
[root@sakura ~]# awk '{print FILENAME}' /etc/issue
/etc/issue
/etc/issue
/etc/issue
[root@sakura ~]# awk 'FNR==1{print FILENAME}' /etc/issue /etc/fstab
/etc/issue
/etc/fstab
ps:需要注意,指定文件有多少行,就会显示多少行文件名,使用操作符FNR==1可仅显示一行文件名
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
9)ARGC:命令行参数的个数
[root@sakura ~]# awk '{print ARGC}' /etc/fstab /etc/issue
3
- 1
- 2
10)ARGV:表示数组,保存的是命令行所给定的各个参数
[root@sakura ~]# awk 'NR==1{print ARGV[0]}' /etc/fstab /etc/issue
awk
[root@sakura ~]# awk 'NR==1{print ARGV[1]}' /etc/fstab /etc/issue
/etc/fstab
[root@sakura ~]# awk 'NR==1{print ARGV[2]}' /etc/fstab /etc/issue
/etc/issue
ps:程序段不会记录于其数组中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
5.aw自定义变量
1)使用选项定义变量;-v var=value;变量名区分字符大小写
[root@sakura ~]# awk -v stars=yuki '{print stars}' /etc/issue
yuki
yuki
yuki
ps:这里文件内容未起作用,但是使用了文件的行数
- 1
- 2
- 3
- 4
- 5
2)在program中直接定义变量
[root@sakura ~]# awk 'BEGIN{FS=":";f1=3}{print $f1}' /etc/passwd
0
1
[root@sakura ~]# awk 'BEGIN{stars="yuki";print stars}'
yuki
- 1
- 2
- 3
- 4
- 5
6.printf格式化输出命令:awk的输出命令之二
1)语法:printf FORMAT, item1, item2, …
1.FORMAT必须要给出
2.printf不会自动实现换行,需要显示给出换行控制符,\n
3.FORMAT中需要分别为后面的每个item指定一个格式化符号
- 1
- 2
- 3
2)格式符对照表
格式符 | 含义 |
---|---|
%c | 显示字符的ASCII码 |
%d,%i | 显示十进制整数 |
%e,%E | 科学计数法数值显示 |
%f | 显示为浮点数 |
%g,%G | 以科学计数法或浮点形式显示数值 |
%s | 显示为字符串 |
%u | 显示为无符号整数 |
%% | 显示%自身 |
3)修饰符
1.#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度;例如,%3.1f,显示为浮点数
2.-:表示左对齐
3.+:显示数值的符号
ps:默认为右对齐
- 1
- 2
- 3
- 4
4)练习
使用printf控制符
[root@sakura ~]# awk -F: '{printf "usernmae:%s,UID:%d\n",$1,$3}' /etc/passwd
usernmae:root,UID:0
usernmae:bin,UID:1
usernmae:daemon,UID:2
ps:运行时会将$1,$3分别对应至%s,%d上
- 1
- 2
- 3
- 4
- 5
- 6
使用printf修饰符
[root@sakura ~]# awk -F: '{printf "usernmae: %-15s,UID: %d\n",$1,$3}' /etc/passwd
usernmae: root ,UID: 0
usernmae: bin ,UID: 1
%-15s表示为,左对齐,%s的宽度为15
- 1
- 2
- 3
- 4
- 5
7.操作符
1)算术操作符:
1.x+y, x-y, x*y, x/y, x^y, x%y
2.-x:转换为负数
3.+x:将字符串转换为数值
- 1
- 2
- 3
2)字符串操作符:没有符号的操作符,表示为字符串连接
3)赋值操作符
1.=, +=, -=, *=, /=, %=, ^=
2.++, --
- 1
- 2
4)比较操作符
>, >=, <, <=, !=, ==
- 1
5)模式匹配符
1.~:是否被模式所匹配
2.!~:是否被模式不匹配
- 1
- 2
6)逻辑操作符
1.&&
2.||
3.!
- 1
- 2
- 3
7)函数调用
function_name(argu1,argu2,....)
- 1
8)条件表达式
selector?if-true-expression:if-false-expression
- 1
9)练习判断/etc/passwd文件中每个用户的UID,若其大于1000则显示common user,否则显示sysuser or sysadmin
[root@sakura ~]# awk -F: '{$3>=1000?usertype="common user":usertype="sysuser or sysadmin";printf "%15s:%-s\n",$1,usertype}' /etc/passwd
root:sysuser or sysadmin
bin:sysuser or sysadmin
daemon:sysuser or sysadmin
- 1
- 2
- 3
- 4
8.PATTERN:模式
1)empty:空模式,匹配所有行
2)/regular expression/:仅处理能够被此处的模式匹配到的行
[root@sakura ~]# awk '/^UUID/{print $1}' /etc/fstab
UUID=366bd0a8-f2cc-4cc6-beaa-418dacdae36f
UUID=274b447f-b701-439b-908a-f328de77cf63
UUID=f9fc6d2d-a8d1-4f08-9aba-8999f3da565c
UUID=362e5f41-bc95-46ff-9902-d4e5c634ad99
[root@sakura ~]# awk '!/^UUID/{print $1}' /etc/fstab
#
#
[root@sakura ~]# awk -F: ‘/1/{print $1,$3}’ /etc/passwd
bin 1
adm 3
abrt 173
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
3)relational expression 关系表达式:结果有“真”或“假”;结果为“真”才会被处理;
结果为非0数值或非空字符串,即可认为是"真"
[root@sakura ~]# awk -F: '$3>=1000{print $1,$3}' /etc/passwd
nfsnobody 65534
yuki 1000
tom 1001
[root@sakura ~]# awk -F: '$1~/root/{print $1,$3}' /etc/passwd
root 0
[root@sakura ~]# awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
root /bin/bash
yuki /bin/bash
[root@sakura ~]# awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd
root /bin/bash
yuki /bin/bash
tom /bin/bash
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
4)line ranges:行范围
1.startlin,endline:/part1/,/part2/
[root@sakura ~]# awk -F: '/^root/,/^adm/{print $1}' /etc/passwd
root
bin
daemon
adm
2.基于行数判断
[root@sakura ~]# awk -F: '(NR>=1&&NR<=5){print $1}' /etc/passwd
root
bin
daemon
adm
lp
ps:不支持直接给出数字的格式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
5)BEGIN/END模式
1.BEGIN{}:仅在开始处理文件中的文本之前执行一次
2.END{}:仅在文本处理完成之后执行一次
[root@sakura ~]# awk -F: 'BEGIN{print " username uid \n-----------------"}{printf "%-10s %10d\n",$1,$3}END{print "===========\n end "}' /etc/passwd
username uid
-----------------
root 0
bin 1
daemon 2
.
.
.
abc8 1013
abc9 1014
abc10 1015
===========
end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
9.常用的action
1.Expressions(表达式)
2.Control statements:if, while等
3.Compound statements:组合语句(将多个语句当做当个代码块运行,多个语句需要使用{})
4.input statements
5.output statements
- 1
- 2
- 3
- 4
- 5
二.控制语句
1.常用控制语句格式
1.if(条件:condition) {条件为真时语句:statments}
2.if(condition) {statments} else {statements} (双分支语句,组合语句中若有一个以上语句需要使用{},否则可以省略{})
3.while(conditon) {statments}
4.do {statements} while(condition) (不论条件是否为真,都先循环一次循环体)
5.for(expr1;expr2;expr3) {statements}
6.break
7.continue
8.delete array[index] (从数组中删除指定语句)
9.delete array
10.exit (退出语句)
11.{ statements }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
2.if-else语句:if(condition) {statement} else {statement}
1)判断/etc/passwd文件中每个用户的UID,若其大于1000则显示common user,否则显示sysuser or sysadmin
print打印
[root@sakura ~]# awk -F: '{if($3>=1000){print "common user:",$1,$3}else{print "sysuser or sysadmin:",$1,$3}}' /etc/passwd
sysuser or sysadmin: root 0
sysuser or sysadmin: bin 1
common user: abc9 1014
common user: abc10 1015
printf打印
[root@sakura ~]# awk -F: '{if($3>=1000){printf "common user: %s,%d\n",$1,$3}else{printf "sysuser or sysadmin: %s,%d\n",$1,$3}}' /etc/passwd
sysuser or sysadmin: root,0
sysuser or sysadmin: bin,1
sysuser or sysadmin: daemon,2
common user: abc9,1014
common user: abc10,1015
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2)判断/etc/passwd文件中每个用户的的bash是否为/bin/bash,并输出
[root@sakura ~]# awk -F: '{if($NF=="/bin/bash"){print $1,$NF}}' /etc/passwd
root /bin/bash
yuki /bin/bash
tom /bin/bash
- 1
- 2
- 3
- 4
3)判断/etc/fstab文件中每行的字段是否大于5,大于5的话则输出整行
[root@sakura ~]# awk '{if(NF>=5){print $0}}' /etc/fstab
# Created by anaconda on Mon May 6 16:16:15 2019
# Accessible filesystems, by reference, are maintained under '/dev/disk'
- 1
- 2
- 3
4)判断df -h命令显示的文件系统用量中,已用量超过20%的分区,并显示之
[root@sakura ~]# df -h | awk -F% '/^\/dev/{print $1}' | awk '{if($NF>=20){print $1,$NF}}'
/dev/sda3 44
/dev/sda2 26
- 1
- 2
- 3
ps:使用场景为,对awk取得的 整行或某个字段做条件判断
3.while循环:while(condition){statement};条件为真时进入循环,条件为假时,退出循环
1)判断/etc/grub2.cfg中,linux16开头的行中的每个单词的字符数
[root@sakura ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.673.10.67.2-el7 29
root=UUID=366bd0a8-f2cc-4cc6-beaa-418dacdae36f 46
ro 2
- 1
- 2
- 3
- 4
- 5
2)判断/etc/grub2.cfg中,显示linux16开头的行中的每个单词的字符数大于等于7的单词
[root@sakura ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){if(length($i)>=7){print $i,length($i)};i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.673.10.67.2-el7 29
root=UUID=366bd0a8-f2cc-4cc6-beaa-418dacdae36f 46
crashkernel=auto 16
LANG=en_US.UTF-8 16
linux16 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3)列出/etc/grub2.cfg文件中,当前系统可使用的内核,并对其编号
[root@sakura ~]# awk -F\' '/^menuentry/{print i++":",$2}' /etc/grub2.cfg
0: CentOS Linux (3.10.673.10.67.2-el7) 7 (Core)
1: CentOS Linux (3.10.67linux-3.10.67-1-el7) 7 (Core)
2: CentOS Linux (3.10.0-693.el7.x86_64) 7 (Core)
3: CentOS Linux (0-rescue-114408c9377741bc91b9e27df3c651f0) 7 (Core)
- 1
- 2
- 3
- 4
- 5
ps:对一行内的多个字段逐一做类似处理时使用;对数组中的各元素逐一处理时使用
4.do-while循环:do {statement}while{condition}
1)意义为:在进入while循环前,至少运行一次循环体
5.for循环:for(expr1;expr2;expr3){statement}
1)语法:for(variable assignment;condition;iteration process) {for-body}
2)判断/etc/grub2.cfg中,linux16开头的行中的每个单词的字符数
[root@sakura ~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++){print $i,length($i)}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.673.10.67.2-el7 29
root=UUID=366bd0a8-f2cc-4cc6-beaa-418dacdae36f 46
ro 2
crashkernel=auto 16
- 1
- 2
- 3
- 4
- 5
- 6
3)特殊用法:能够实现遍历数组中的元素
语法:for(var in array){for-body}
- 1
6.switch语句(类似bash shell中的case语句)
1)语法:switch(expression){case VALUE1 or /REGEXP/:statement;case VALUE2 or /REGEXP/:statement;…;default:statement}
ps:VALUE1 or /REGEXP为变量值或正则表达式能匹配到的内容;default为当所有选项不匹配时,显示的结构
7.break和continue
1)break [n]:可跳出n层循环
2)continue:提前结束本轮循环,进入下轮循环
8.next:跳出awk的内置循环,即提前结束对本行的处理而直接进入下一行
1)输出/etc/passwd文件中用户ID为偶数的用户,使用next实现
[root@sakura ~]# awk -F: '{if($3%2!=0){next}else{print $1,$3}}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
ps:使用awk -F: '{if($3%2!=0)next;print $1,$3}' /etc/passwd也可以实现
- 1
- 2
- 3
- 4
- 5
- 6
- 7
三.array数组
1.awk中一般使用关联数组:array[index-expression]
1)index-expression,数组索引
1.可使用任意字符串;字符串需要使用双引号
2.如果某数组元素实现不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
3.若需要判断数组中某元素是否存在,需要使用"index in array"格式进行,不能直接判断数组中的元素是否为空
[root@sakura ~]# awk 'BEGIN{role["riben"]="kasumi";role["china"]="wanglin";print role["riben"]}'
kasumi
[root@sakura ~]# awk 'BEGIN{role["riben"]="kasumi";role["china"]="wanglin";print role["china"]}'
wanglin
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2.若需要遍历数组中的每个元素,需要使用for循环
1)for(var in array){for-body}
[root@sakura ~]# awk 'BEGIN{role["riben"]="kasumi";role["china"]="wanglin";for(i in role){print role[i]}}'
kasumi
wanglin
[root@sakura ~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
Tuesday
Monday
ps:var会遍历array的每一个索引
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2)统计netstat -tan命令中tcp协议各个状态的出现次数
[root@sakura ~]# netstat -tan | awk '/^tcp\>/{stat[$NF]++}END{for(i in stat){print i,stat[i]}}'
LISTEN 5
ESTABLISHED 3
- 1
- 2
- 3
3)统计/var/log/httpd/access_log文件中,各个ip访问的次数
[root@sakura ~]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
192.168.3.19 72
- 1
- 2
4)统计/etc/fstab文件中每个文件系统类型出现的次数
[root@sakura ~]# awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab
swap 1
xfs 3
- 1
- 2
- 3
5)统计指定文件中每个单词出现的次数
[root@sakura ~]# awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab
man 1
May 1
and/or 1
maintained 1
xfs 3
file 1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
四.awk内置函数
1.数值处理:rand():返回0和1之间一个随机数
[root@sakura ~]# awk 'BEGIN{print rand()}'
0.237788
- 1
- 2
2.字符串处理
1)length([s]):返回指定字符串的长度
2)sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容
[root@sakura ~]# awk -F: '{print sub(o,O,$1)}' /etc/passwd
1
1
1
ps:显示结果为显示是否替换成功
- 1
- 2
- 3
- 4
- 5
3)gsubsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容
4)split(s,a,[r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中
统计客户端访问http服务的次数
[root@sakura ~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
192.168.3.19 3
0.0.0.0 5
- 1
- 2
- 3
- 4
ps:awk也可使用自定义函数
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e9f16cbbc2.css" rel="stylesheet">
</div>
</article>
ab ↩︎