一.AWK介绍
awk属于一种脚本语言,是一个报告生成器,由 GUN/Linux 自由软件资金会(FSF)进行开发和维护,通常也叫做 gawk(GNU AWK);用于在Linux平台或者Unix平台下对数据进行处理,数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。awk和grep,sed命令不同点:grep,sed命令是对数据逐行处理,而awk可以对数据指定分割字符按列处理。此外:awk命令支持自定义变量,算数运行,字符串处理,循坏条件判断,数组以及函数等内置功能。
awk的处理文本和数据的方式: 逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
二.AWK的作用
1.awk用来处理文件和数据的,是类unix下的一个工具,也是一种编程语言
2.可以用来统计数据,比如网站的访问量,访问的IP量等等
3.支持条件判断,支持for和while循环
三.AWK语法说明
语法格式
awk [options] 'program' file(s)
awk [options] -f programfile var=value file(s)
说明:引用shell变量需要用双引号引起,program需要放在单引号中的。
常用命令选项
-F fs fs指定输入分隔符,默认分隔符是匹配连续的空白符,支持正则匹配进行分割,如-F:
-v var=value 赋值一个用户定义变量,将外部变量传递给awk
-f scripfile 从脚本文件中读取awk命令
awk通过指定处理模式program对数据进行处理;通常情况下处理模式program放在单引号中,program格式如下:
'pattern {action string}'
pattern模式:决定了何时触发处理数据的动作
- BEGIN模式:用于awk处理数据之前做对应操作,比如:打印标题,变量初始化
- 正则匹配模式:awk默认模式,用于匹配对的数据进行处理;不指定匹配条件的话,则处理全部数据行。
- END模式:用于awk处理数据后做对应操作,比如:统计总和
action :定义处理数据的动作(操作),处理操作要写到大括号中,常见的操作print ,printf.
案例
1>正则表达式,地址定位
'/root/{awk语句]' sed中: '/root/p'
'NR==1,NR==5{awk语句}' sed中 : '1,5p'
'/^root/,/^ftp/{awk语句}' sed中: '/^root/,/^ftp/p
2>{awk语句1;awk语句2;...}
'{print $0;print $1}' sed中: 'p'
'NR==5{print $0}' sed中:'5p'
3>BEGIN...END....
'BEGIN{awk语句};{处理中};END{awk语句}'
'BEGIN{awk语句};{处理中}'
'{处理中};END{awk语句}'
四.AWK工作原理
awk执行流程
第一步:执行BEGIN模式对应的操作
第二步:从文件或标准输入(stdin)读取一行数据,满足匹配条件进行处理,不符合条件的继续读取下一行数据,直到数据全部读取完毕。
第三步:全部数据处理完毕之后,执行END模式对应的操作
4.1 处理动作 print
4.1.1 处理动作 print 语法
'<模式>{print item1,item2.....}''
说明
- 如果不指定模式,默认为匹配模式,且匹配整行数据
- item输出信息可以是字符串,数字,也可以是分割的字段,变量,awk表达式
- 如果指定输出信息是固定字符串,建议使用双引号引起来!
- 按照分割符,第一列为
$1
,第二列为$2
…依次类推,$0
表示输出整行数据 - 如果不知道item输出信息,默认输出匹配到的整行数据
4.1.2 示例
示例:把标准输入数据替换为固定字符串hello gawk
awk '{print "hello,gawk"}'
示例:把seq 10
生成的10条数据替换为固定字符串hello gawk
seq 10 | awk '{print "hello gawk"}'
示例:将标准输入内容替换成算术运算符的内容
seq 10 |awk '{print 2*3}'
示例:指定awk分割符为冒号:,把/etc/passwd
文件中整行输数据替换为hello gawk
awk -F: '{print "hello gawk"}' /etc/passwd
示例:指定awk分割符为冒号:打印/etc/passwd
文件中整行数据
awk -F: '{print}' /etc/passwd
awk -F: '{print $0}' /etc/passwd
示例:打印/etc/passwd
以指定awk分割符为冒号:,第一列和第三列信息
awk -F: '{print $1$3}' /etc/passwd
说明:awk默认打印多个列之间紧挨着,要想把多个列之间分割,如下方法
- 多个列之间加入固定字符作为分割
- 多个列之间加入一个逗号,在awk的动作中逗号表示空格
awk -F: '{print $1" "$3}' /etc/passwd
awk -F: '{print $1,$3}' /etc/passwd
awk -F: '{print $1"\t"$3}' /etc/passwd
示例:取磁盘空间利用率
df|awk '{print $1,$5}'
按照上面的方法,可以取出利用率,但是利用路后面带有%符号,如何把百分号去掉?
df|awk '{print $1,$5}'|tr -d "%"
df|awk -F' +|%' '{print $1,$5}'
df|awk -F'[[:space:]]+|%' '{print $1,$5}'
只取出真正磁盘的利用率,而虚拟磁盘利用率则舍去
df|awk -F' +|%' '{print $1,$5}' |grep '^/dev'
df|awk -F' +|%' '/^\/dev/{print $1,$5}'
4.2 处理动作 printf
printf处理动作:实现格式化输出信息
awk '{printf "FORMAT",expr-list}'
说明
- FORMAT:指定格式化的格式,必须指定格式化格式
- expr-list:指定输出的信息列表,列表每个信息对应格式符
- printf:默认不自动换行,需要格式化输出后面加上换行符
\n
FORMAT格式化输出有俩部分组成:格式符 + 修饰符
格式符 | 作用 |
---|---|
%c | 显示字符的ASCII码 |
%d %i | 显示十进制整数 |
%e %E | 以科学计数法显示 |
%f | 显示浮点数,即:小数 |
%s | 以字符串显示 |
%u | 显示无符号的整数 |
%% | 显示百分号% |
修饰符
#[.#] 第一个数字控制显示的宽度(默认右对齐);
第二个#对于小数表示小数点后精度, 如:%3.1f; 对于整数表示整数位数,不足则前面补0,如:%.3d
- 左对齐宽度, 如:%-15s
+ 显示数值的正负符号 如:%+d
示例
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 "%-20s | %10d\n",$1,$3}' /etc/passwd
awk -F: 'BEGIN{print "---------------"}{printf "%-20s | %10d\n",$1,$3}' /etc/passwd
awk -F: 'BEGIN{print "---------------"}{printf "%-20s | %10d\n",$1,$3}END{print "************************"}' /etc/passwd
awk -F: '{printf "username:%-20s userid:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "username:%-20s userid:%.3f\n",$1,$3}' /etc/passwd
awk -F: '{printf "username:%-20s userid:%.3d\n",$1,$3}' /etc/passwd
五、awk变量
5.1 内置变量
awk命令中的变量分为:awk内置变量和自定义变量,内置变量如下
awk内置变量 | 作用 | 备注 |
---|---|---|
$0 | 当前处理行的所有记录 | |
$1,$2,3...3...N | 文件中每行以间隔符分割的不同字符 | awk -F ‘{print $1,$2}’ |
FS | 定义awk分割符,功能相当于 awk -F 参数,如果俩者同时使用,其后面生效。 | 'BEGIN{FS=":"};{print $1,$3} |
OFS | 定义awk输出的分隔符,默认为空隔字符。列与列之间逗号分割,输出时会把逗号替换为OFS变量值 | ‘BEGIN{OFS="****"};{print 1,1,NF}’ |
RS | 定义输入记录的分隔符,默认情况下以换行符作为记录分隔符;awk按照一条记录进行处理(具体看示例) | ‘BEGIN{RS=";"};{print $0}’ |
ORS | 定义输出记录之间的分隔符,默认情况下以换行符作为分隔符 | |
NF | 表示一条记录按照FS分隔符分割了多少个字段 | awk -F: ‘{print NF}’ |
NR | 对每一条记录从1开始进行编号 | awk -F: ‘NR==1{print $1}’ |
FNR | awk命令默认接收多个文件连续按每一行进行编号,FNR是按照每个文件独立从1开始编号 | |
FILENAME | 显示当前记录所在文件名 |
$0变量
awk -F: '{print $0}' /etc/passwd
等同于:
awk -F: '{print}' /etc/passwd
1..1..N变量
awk -F: '{print $1,$5}' /etc/passwd
awk -F: '{print $1,$NF}' /etc/passwd
FS变量
#定义FS变量值作为awk分隔符
awk -v FS=':' '{print $1FS$3}' /etc/passwd
awk 'BEGIN{FS=":"};{print $1}' /etc/passwd
#处理数据动作调用awk变量,awk调用内置变量不需要加$符号
awk -v FS=":" '{print $1FS$3}' /etc/passwd
awk -F: '{print $1FS$3}' /etc/passwd
#awk调用shell中变量
S=:;awk -v FS=$S '{print $1FS$3}' /etc/passwd
#-F 和 FS变量功能一样,同时使用会冲突,则后面定义生效
awk -v FS=":" -F";" '{print $1FS$3}' /etc/passwd |head -n3
awk -F";" -v FS=":" '{print $1FS$3}' /etc/passwd |head -n3
OFS变量
awk -F: -v OFS='---' '{print $1,$NF}' /etc/passwd
awk -F: 'BEGIN{OFS=";"}{print $1,$NF}' /etc/passwd
awk 'BEGIN{FS=":";OFS="---"}{print $1,$NF}' /etc/passwd
# 直接在print中定义输出分割符“”引起来
awk 'BEGIN{FS=":"};{print $1"*****"$NF}' /etc/passwd
awk 'BEGIN{FS=":"};{print "username:"$1"*****""登录shell:"$NF}' /etc/passwd
RS变量和ORS
#看如下文件内容
[root@centos7 ~ ]# cat test.txt
AA,BB;CC,DD
EE,FF;GG,HH
#awk默认以换行符作为一条记录进行处理
[root@centos7 ~ ]# awk -v FS=',' -v OFS=' --- ' '{print $1,$3}' test.txt
AA --- DD
EE --- HH
#当设置RS为; 表示以分号作为awk处理记录的分隔符, 也就是: AA,BB,CC 是处理一条记录, DD\nEE,FF 是另一条记录, GG,HH\nII 一条记录等
[root@centos7 ~ ]# awk -v FS=',' -v OFS=' --- ' -v RS=';' '{print $1,$2}' test.txt
AA --- BB
CC --- DD
EE
GG --- HH
[root@centos7 ~ ]# awk -v FS=',' -v OFS=' --- ' -v RS=';' -v ORS='+++' '{print $1,$2}' test.txt
AA --- BB+++CC --- DD
EE+++GG --- HH
+++
NF变量
# 打印列数
awk -v FS=: '{print NF}' /etc/passwd
#通过 NF 变量取/etc/passwd文件的倒数第二列数据
awk -v FS=: '{print $(NF-1)}' /etc/passwd
#通过 NF 变量取/etc/passwd文件的倒数第1列数据
awk -v FS=: '{print $NF}' /etc/passwd
#打印包含root的行
awk -F: '/root/{print $1,$NF}' /etc/passwd
面试题:统计当前访问服务器IP的前三名访问次数
ss -nt|grep '^ESTAB'|tr -s ' ' |cut -d' ' -f5|cut -d':' -f1|sort|uniq -c|sort -nr|head -n3
ss -nt|awk -F' +|:' '/ESTAB/{print $(NF-2)}'|sort|uniq -c|sort -nr|head -n3
面试题:将访问量前3的IP地址拉入黑名单
#!/bin/bash
IP_LIST=` ss -nt|grep '^ESTAB'|tr -s ' ' |cut -d' ' -f5|cut -d':' -f1|sort|uniq -c|sort -nr|head -3|awk '{print $2}'`
for ip in $IP_LIST;do
iptables -I INPUT -p tcp -s $ip -j REJECT
done
NR变量,取/etc/passwd
文件的第二行信息
# NR利用 == 判断对应行号
[root@centos7 ~ ]# awk 'NR==2' /etc/passwd
打印第1列到第5列
[root@centos7 ~ ]# awk 'NR==1,NR==5{print}' /etc/passwd
[root@centos7 ~ ]# awk 'NR>=1 && NR<=5{print}' /etc/passwd
打印第1列和第5列
[root@centos7 ~ ]# awk 'NR==1;NR==5{print}' /etc/passwd
[root@centos7 ~ ]# awk 'NR==1 || NR==5{print}' /etc/passwd
打印1-5行包含以root开头的行
[root@centos7 ~ ]#awk 'NR==1, NR==5;/^root/{print}' /etc/passwd
面试题:取出ifconfig显示结果的IP地址
[root@centos7 ~ ]# ifconfig ens32|awk '/netmask/{print $2}'
192.168.10.100
[root@centos7 ~ ]# ifconfig ens32|awk 'NR==2{print $2}'
192.168.10.100
grep和sed实现
[root@centos7 ~ ]#ifconfig ens32|sed -rn '2s/^[^0-9]+([0-9.]+) .*$/\1/p'
192.168.10.100
[root@centos7 ~ ]# ifconfig ens32 |grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}'|head -1
192.168.10.100
FNR变量
[root@centos7 ~]# awk -F: '{print NR,$0}' passwd.txt /etc/hosts
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
12 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@centos7 ~]# awk -F: 'BEGIN{OFS="---"};{print NR,$0}' passwd.txt /etc/hosts
1---root:x:0:0:root:/root:/bin/bash
2---bin:x:1:1:bin:/bin:/sbin/nologin
3---daemon:x:2:2:daemon:/sbin:/sbin/nologin
4---adm:x:3:4:adm:/var/adm:/sbin/nologin
5---lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6---sync:x:5:0:sync:/sbin:/bin/sync
7---shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8---halt:x:7:0:halt:/sbin:/sbin/halt
9---mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10---operator:x:11:0:operator:/root:/sbin/nologin
11---127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
12---::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@centos7 ~ ]# awk '{print FNR,$0}' passwd.txt /etc/hosts
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
1 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
2 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@centos7 ~]# awk -F: 'BEGIN{OFS="---"};{print FNR,$0}' passwd.txt /etc/hosts
1---root:x:0:0:root:/root:/bin/bash
2---bin:x:1:1:bin:/bin:/sbin/nologin
3---daemon:x:2:2:daemon:/sbin:/sbin/nologin
4---adm:x:3:4:adm:/var/adm:/sbin/nologin
5---lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6---sync:x:5:0:sync:/sbin:/bin/sync
7---shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8---halt:x:7:0:halt:/sbin:/sbin/halt
9---mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10---operator:x:11:0:operator:/root:/sbin/nologin
1---127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
2---::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
FILENAME变量:显示当前记录所在文件名
[root@centos7 ~]# awk '{print FNR,FILENAME,$0}' passwd.txt /etc/hosts
1 passwd.txt root:x:0:0:root:/root:/bin/bash
2 passwd.txt bin:x:1:1:bin:/bin:/sbin/nologin
3 passwd.txt daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 passwd.txt adm:x:3:4:adm:/var/adm:/sbin/nologin
5 passwd.txt lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 passwd.txt sync:x:5:0:sync:/sbin:/bin/sync
7 passwd.txt shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 passwd.txt halt:x:7:0:halt:/sbin:/sbin/halt
9 passwd.txt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 passwd.txt operator:x:11:0:operator:/root:/sbin/nologin
1 /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
2 /etc/hosts ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
5.2 自定义变量
awk命令中支持自定义变量
- awk -v 用于自定义awk变量,格式为:awk -v var=value
- 在
program
的处理动作action
中直接定义,注意点:多个处理动作以;分号隔开
[root@centos7 ~]#awk -v test='hello world' '{print test}' /etc/issue
[root@centos7 ~]#awk -v test='hello world' 'BEGIN{print test}'
[root@centos7 ~]#awk '{test="hello world";print test}' /etc/issue
注意:awk中不使用参数 -v 声明变量时,在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v 参数,让awk在执行 BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数
[root@centos7 ~]# awk a=10 'BEGIN{print a}'
awk: fatal: cannot open file `BEGIN{print a}' for reading (No such file or directory)
[root@centos7 ~]# awk -v a=10 'BEGIN{print a}'
10
示例:从脚本文件中读取awk命令
[root@centos7 ~]# cat test.awk
BEGIN{OFS="****"};{print $1,$NF}
[root@shell ~]# awk -F: -f test.awk /etc/passwd
root****/bin/bash
bin****/sbin/nologin
daemon****/sbin/nologin
adm****/sbin/nologin
lp****/sbin/nologin
sync****/bin/sync
shutdown****/sbin/shutdown
halt****/sbin/halt
mail****/sbin/nologin
示例
#说明:age赋值处于处理操作print后面,会赋值给下次处理操作变量age
[root@centos7 ~ ]# awk -F: '{sex="male";print $1,sex,age;age=age+1}' /etc/passwd
root male
bin male 1
daemon male 2
adm male 3
六、awk运算与判断
6.1 算数运算
运算符 | 描述 |
---|---|
a + b | 相加运算 |
a - b | 相减运算 |
a * b | 相乘运算 |
a / b | 相除运算 |
a ^ b == a ** b | 次幂运算 |
a % b | 取模运算 |
-a | 将正数转换为负数 |
注意:所有用作算术运算符进行操作,操作数自动转为数值,所有非数值都变为0
[root@centos7 ~ ]# awk 'BEGIN{a="a";print a++,++a}'
0 2
注意:将正数转换为负数
[root@centos7 ~ ]#awk 'BEGIN{n=1;print -n,-++n}'
6.2 赋值运算
运算符 | 描述 |
---|---|
+= | 加法赋值运算,a += 1 相当于 a = a+1 |
-= | 减法赋值运算,a -= 1 相当于 a = a -1 |
*= | 乘法赋值运算 |
/= | 除法赋值运算 |
^= | 次幂赋值运算 |
%= | 取模赋值运算 |
i++ ,i– | 先赋值,再运算 |
++i ,–i | 先运算,再赋值 |
示例
[root@centos7 ~ ]#awk 'BEGIN{i=0;print i++,i}'
[root@centos7 ~ ]#awk 'BEGIN{i=0;print ++i,i}'
6.3 比较运算
运算符 | 描述 |
---|---|
a == b | 判断a和b是否相等 |
a != b | 判断a和b是否不相等 |
a > b | 判断a是否大于b |
a >= b | 判断a是否大于等于b |
a < b | 判断a是否小于b |
a <= b | 判断a是否小于等于b |
示例:只查看/etc/passwd
文件的第二行数据
[root@centos7 ~ ]#awk 'NR==2' /etc/passwd
示例:查看/etc/passwd
文件中uid大于1000的数据行
[root@centos7 ~ ]#awk -F: '$3>=1000' /etc/passwd
面试题:请将UID>1000的用户删除
#!/bin/bash
ID=`awk -F: '$3>=1000{print $1}' /etc/passwd`
for i in $ID;do
userdel -r $i
done
示例:打印文件奇数行数据和偶数行
#偶数行
[root@centos7 ~ ]#awk 'NR%2==0' /etc/passwd
[root@centos7 ~ ]#awk 'NR%2!=1' /etc/passwd
#奇数行
[root@centos7 ~ ]#awk 'NR%2==1' /etc/passwd
[root@centos7 ~ ]#awk 'NR%2!=0' /etc/passwd
6.4 逻辑运算
运算符 | 描述 |
---|---|
&& | 逻辑与,&&符号俩边运算都为真 |
|| | 逻辑或,||符号俩边运算一边为真即可 |
! | 取反操作 |
示例
[root@centos7 ~ ]#awk -F: '$3>=0 && $3<=1000 {print $1,$3}' /etc/passwd
[root@centos7 ~ ]#awk -F: '$3==0 || $3>=1000 {print $1,$3}' /etc/passwd
[root@centos7 ~ ]#awk -F: '! $3==0 {print $3}' /etc/passwd
取反操作
awk -v n=0 'BEGIN{print !n}'
awk -v n=5 'BEGIN{print !n}'
awk -v n=-2 'BEGIN{print !n}'
awk -v n='abc' 'BEGIN{print !n}'
awk -v n=' ' 'BEGIN{print !n}'
awk -v n='' 'BEGIN{print !n}'
awk -v n=0 '!n++' /etc/passwd
-------------------------------------------------------------------------------------------
说明:上面命令相当于 awk -v n=0 '!n++{print $0}' /etc/passwd
awk -v定义了n变量,初始化为0, 处理/etc/passwd文件数据
处理第一条记录:计算!n ==> !0 ==> 1 结果为真,则输出一条记录;最后 n++ ==> n=1
在 awk 中,如果 n 的值是 0,则逻辑非 !n 的值为 1;如果 n 的值是 非零数,则逻辑非 !n 的值为 0。因此,无论 n 的值是什么,这个命令都会打印 0
6.5 模式匹配
运算符 | 描述 |
---|---|
NR ~ /pattern/ | 指定awk分割的某一列进行匹配,匹配到信息则进行对应操作 |
NR !~ /pattern/ | 匹配信息取反,相当于 grep -v |
示例
#匹配/etc/passwd文件中每一行含有root字符串则打印
[root@centos7 ~ ]#awk -F: '$0 ~/root/' /etc/passwd
#匹配/etc/passwd文件中每一行不含有root字符串则打印
[root@centos7 ~ ]#awk -F: '$0 !~/root/' /etc/passwd
#匹配/etc/passwd文件中第一列含有root字符串则打印
[root@centos7 ~ ]#awk -F: '$1 ~/root/' /etc/passwd
6.6 三目运算
awk三目运算和C语言的三目运算相同,当条件为真,则执行操作1,当条件为假,则执行操作2
条件?操作1:操作2
示例
awk -F: '{$3>=1000?usertype="Common User":usertype="SysUser";printf "%-20s:%12s\n",$1,usertype}' /etc/passwd
七、awk模式 pattern
PATTERN:根据pattern条件,过滤匹配的行,再做处理
7.1 空模式
空模式:不指定匹配正则信息,则会处理动作:{print $0}
awk -F: '{print $1}' /etc/passwd
7.2 正则匹配模式
正则匹配模式:根据正则表达式匹配数据行,匹配到数据行进行处理,未能匹配到数据行不做任何处理
awk '/^UUID/' /etc/fstab
awk '$0 ~ /^UUID/' /etc/fstab
awk '! /^UUID/' /etc/fstab
awk '$0 !~ /^UUID/' /etc/fstab
7.3 表达式模式
表达式模式:只有表达式结果为真,则处理对应数据行,如果表达式结果为假,则不对数据行做任何处理
- 表达式为真:非0,非空字符串
- 表达式为假:数字0,或者是空字符串
seq 10 | awk '0'
seq 10 | awk '1'
seq 10 | awk '"gawk"'
seq 10 | awk '""'
seq 10 | awk '" "'
----------------------------------------------------------------------------
#awk命令,字符串前后不加双引号默认认为是变量名,此时var变量未定义,则为假
seq 10 | awk 'var'
#定义了var变量,但未赋于初始值,则为假
seq 10 | awk -v var= 'var'
#awk命令会把变量值替换到表达式中, 也就是:var="0“ 替换到表达式 '0'
seq 10 | awk -v var="0" 'var'
#变量var值为gawk,不为空字符串,则为真,处理所有数据行
seq 10 | awk -v var="gawk" 'var'
示例
awk -F: 'i=1;j=1{print i,j}' /etc/passwd
awk -F: '$3>=1000{print $1,$3}' /etc/passwd
awk -F: '$3<1000{print $1,$3}' /etc/passwd
awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
7.4 行模式
awk不支持直接用行号,但可以使用变量NR间接指定行号进行判断处理
[root@shell shell]#seq 10 | awk 'NR>=3 && NR<=6'
[root@shell shell]#awk 'NR>=3 && NR<=6{print NR,$0}' /etc/passwd
[root@shell shell]# awk -F: 'NR==1;NR==5' /etc/passwd
root:x:0:0:root:/root:/bin/bash
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@shell shell]# awk -F: 'NR==1||NR==5' /etc/passwd
root:x:0:0:root:/root:/bin/bash
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
awk也支持sed命令的正则范围匹配,匹配一行信息到另一行信息进行处理,比如:匹配以bin开头的行到以sshd开头的行
sed -n '/^bin/,/^sshd/p' /etc/passwd
awk '/^bin/,/^sshd/' /etc/passwd
awk -v FS=":" '/^bin/,/^sshd/{print $1,$3}' /etc/passwd
7.5 BEGIN / END 模式
BEGIN模式:仅在开始处理文件中的文本之前执行一次
END模式:仅在文本处理完成之后执行一次
awk -F: 'BEGIN{print "用户","UUID","家目录"};{print $1,$3,$(NF-1)}' /etc/passwd|column -t
awk -F: 'BEGIN{print "用户","UUID","家目录"};{print $1,$3,$(NF-1)};END{print "数据处理完毕!"}'/etc/passwd|column -t
示例:awk计算
awk 'BEGIN{print 10/3}'
awk -v var=10 'BEGIN{print var/3}
示例:统计/etc/service
文件中的空白行
[root@centos7 ~ ]# egrep "^$" /etc/services|wc -l
17
[root@centos7 ~ ]# awk '/^$/{i=i+1};END{print i}' /etc/services
17
示例:统计/etc/services文件中有井号开头的行
[root@centos7 ~ ]# egrep "^#" /etc/services|wc -l
102
[root@centos7 ~ ]# awk -v i=0 '/^#/{i=i+1};END{print i}' /etc/services
102
[root@centos7 ~ ]# awk '/^#/{i++};END{print i}' /etc/services
102
示例:统计系统中有多少个虚拟用户和普通用户
[root@centos7 ~ ]# awk -F: -v n=0 -v m=0 'BEGIN{printf "%-20s%-s\n","login_user","nologin_user"};/bash$/{n++};!/bash$/{m++};END{printf "%-20s%s\n",n,m}' /etc/passwd
login_user nologin_user
2 21
八、awk流程控制
在linux awk的while、do-while和for语句中允许使用break,continue语句来控制流程走向,也允许使用exit这样的语句来退出。break中断当前正在执行的循环并跳到循环外执行下一条语句。if 是流程选择用法。awk中,流程控制语句,语法结构,与c语言类型。有了这些语句,其实很多shell程序都可以交给awk,而且性能是非常快的。下面是各个语句用法。
8.1 条件判断 if
为了方便判断和阅读,最好将多个语句用{}括起来。awk分枝结构允许嵌套,其格式为:
if(表达式)
{语句1}
else if(表达式)
{语句2}
else
{语句3}
注意:每条语句块可以忽略大括号,但建议加上,便于分辨。
awk 'BEGIN{ test=100;
if(test>90)
{print "very good"}
else if(test>60)
{print "good"}
else{print "no pass"}}'
示例:判断/etc/passwd
文件中用户UID大于等于1000的用户打印出来
awk -F: '{
if($3>=1000)
{print $1,$3}
}' /etc/passwd
示例 :判断/etc/passwd
文件中用户类型,普通用户则前面输出Commin user
,系统用户则前面输出System user
awk -F: '{if($3>100)print "Common user:" $1,$3;else print "System user:"$1,$3 }' /etc/passwd
示例:判断磁盘利用率大于30%的输出对应分区
df|awk -F" +|%" '/^\/dev/{if($5>=15) print $1,$5}'
8.2 switch 语句
switch语句类似于shell中的case语句,接收一个变量,根据不同变量值进行不同操作
switch (变量){
case 值1:
语法1;
case 值2:
语法2;
......
default:
语法N
}
示例
awk 'BEGIN{ test=100;
switch (test) {
case 100:
{print "very good";break}
case 80:
{print "good";break}
default:
{print "no pass"}
}
}'
8.3 循环 while
while语法
while (条件)
{语句块}
当条件为真,执行语句块的命令;当条件为假,则不执行语句块命令,继续判断下个条件。
示例:过滤/etc/passwd
文件中以root开头的行,输出每一列数据的字符长度
awk -F: '/^root/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/passwd
awk -F: '/^root/{i=1;while(i<=NF){if(length($i)>=4)print $i,length($i);i++}}' /etc/passwd
面试题:计算1~100和,awk与shell脚本中的while,for循环速度比较快。
seq -s+ 100|bc
i=1;sum=0;while [ $i -le 100 ];do let sum+=i; let i++ ;done;echo $sum
sum=0;for i in {1..100};do let sum+=i;done;echo $sum
awk 'BEGIN{i=1;sum=0;while(i<101){sum+=i;i++};print sum}'
疑问:那for循环和awk计算1000000的值相加,谁的速度更快呢?
8.4 循环 do-while
do-while循环:先执行语句块,再进行条件的判断,所以do-while循环无论条件是否为真,至少执行一次语句块中的命令。
do {
语句块
}while (条件)
示例:
awk 'BEGIN{sum=0;i=1;do{ sum+=i;i++ }while(i<=100);print sum}'
8.5 循环 for
awk命令的for循环支持俩种格式:一种是shell风格,一种是C语言风格
shell风格的for循环:能够遍历awk数组中的元素
for(变量 in 数组)
{语句}
C语言风格的for循环
for(变量;条件;表达式)
{语句}
示例
awk 'BEGIN{for(i=0;i<101;i++){sum+=i};print sum}'
8.6 break 和 continue
break:终止整个循环
continue:终止本次循环,继续执行下次循环体
[root@centos7 ~ ]# awk 'BEGIN{for(i=0;i<101;i++){if(i==50)break; else sum+=i};print sum}'
1225
[root@centos7 ~ ]# awk 'BEGIN{for(i=0;i<101;i++){if(i%2==0){continue} else {sum+=i}};print sum}'
2500
8.7 next 和 exit
awk命令本身就是循环读取文件内容按照指定的动作处理,next和exit控制awk命令自身的循环
next:相当于循环的continue的功能,在awk处理记录时,处理执行遇到next就不再执行后续处理操作,直接处理下一条记录
exit:语句使主输入循环退出并将控制转移到END,如果END存在的话。如果没有定义END规则,或在END中应用exit语句,则终止脚本的执行
[root@centos7 ~ ]# seq 10|awk '{if($1%2==0)next;print $1}'
1
3
5
7
9
[root@centos7 ~ ]# seq 10|awk '{if($1%2==0)exit;print $1}END{print "Perform the end"}'
1
Perform the end
九、awk数组
9.1 数组赋值取值
awk数组为关联数组,可定义数据下标,以key / vaule取值
weekdays["mon"]="Monday"
注意:
- awk数组为关联数组,实现key / vaule功能
- 可使用任意字符串;字符串要使用双引号括起来
- 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
- 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"],weekdays["tue"],weekdays["wed"]}'
Monday Tuesday
示例:利用awk对文件内容去重
[root@centos7 ~ ]# cat awk.txt
a
bb
ccc
a
ccc
a
[root@centos7 ~ ]# awk '!arr[$0]++' awk.txt
a
bb
ccc
-----------------------------------------------------------------------------------
执行流程说明:awk.txt文件中存在相同的数据行
第一行数据:$0表示取出整行数据即:arr['a'],此数组元素未初始化即为空arr['a']="",先执行取反运算!arr["a"]为真则print $0,执行arr['a']++ 即arr['a']=1
第二行数据:arr['bb'],此数组元素未初始化即为空arr['bb']="",先执行取反运算!arr["bb"]为真则print $0,执行arr['bb']++ 即arr['bb']=1
第三行数据:如上述
第四行数据:此时数据的arr['a']=1,执行取反运算!arr['a']为假则不执行任何操作,执行arr['a']++ 即arr['a']=2
#通过下面命令可查看执行过程
awk '!arr[$0]++;{print $0,arr[$0]}' awk.txt
对于awk数组判断元素是否存在,可以通过数组下标名 in 数组名
判断awk数组中是否存在此下标名,从而判断出数组是否存在对应数组
[root@centos7 ~ ]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print "mon" in weekdays,"wed" inweekdays}'
1 0 # 1表示下表名存在, 0表示不存在
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";if ("mon" in weekdays){print "存在"}else{print "不存在"}}'
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";if ("wed" in weekdays){print "存在"}else{print "不存在"}}'
9.2 数组遍历
由于awk数组采用了关联数组,其下标不按照常规的从0开始取值,awk命令提供了利用for循环对awk数组元素取值
for( i in 数组名){执行语句}
说明:for循环会把awk数组名下边进行遍历,并遍历赋值给var临时遍历,即通过 数组名[var]
取出对于下标元素数值
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays){print weekdays[i]}}'
awk 'BEGIN{students[1]="zhangsan";students[2]="lisi";students[3]="zhaoliu";for(i in students){print i":"students[i]}}'
awk -F: '{user[$1]=$3}END{for(i in user){print "username:"i,"uid: "user[i]}}' /etc/passwd
示例:显示主机的连接状态出现的次数
[root@centos7 ~ ]# ss -ant|awk 'NR!=1'|cut -d" " -f1|sort -r|uniq -c|column -t
6 LISTEN
3 ESTAB
[root@centos7 ~ ]# ss -ant | awk 'NR!=1{state[$1]++}END{for(i in state){print state[i],i}}'
6 LISTEN
3 ESTAB
9.3 多维数组
awk支持多维数组:二维数组,三维数组等等
awk 'BEGIN{
arr[1][1]=11
arr[1][2]=12
arr[2][1]=21
arr[2][2]=22
arr[2][3]=23
for (i in arr)
for (j in arr[i])
print arr[i][j]
}
十、awk函数
10.1 随机数函数 rand & srand & int
rand():返回0和1之间一个随机数
srand():配合rand() 函数,生成随机数的种子
int():返回整数部分
示例:随机生成0和1之间的随机小数
[root@centos7 ~ ]# awk 'BEGIN{srand();print rand()}'
0.366016
[root@centos7 ~ ]# awk 'BEGIN{srand();print rand()}'
0.709647
[root@centos7 ~ ]# awk 'BEGIN{srand();print rand()}'
0.405127
示例:随机生成一个俩位整数
echo $RANDOM%100|bc
cat /dev/urandom | tr -dc '[:digit:]'|head -c2
awk 'BEGIN{srand();print int(rand()*100)}'
10.2 字符串处理
length([s]):返回指定字符串的字符长度
sub(r , s , [ t ] ):对 t
字符串搜索 r
表示模式匹配的内容,并将第一个匹配内容替换为 s
,即:对数据那一列的字符串替换
[root@centos7 ~ ]# echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08
[root@centos7 ~ ]# awk '/test/' /etc/passwd
test:x:1000:1000::/home/test:/bin/bash
[root@centos7 ~ ]# awk 'sub(/test/,"spuertest",$1)' /etc/passwd
spuertest:x:1000:1000::/home/test:/bin/bash
gsub( r , s , [ t ]):对 t
字符串进行搜索 r
表示的模式匹配的内容,并全部替换为 s
所表示的内容
[root@centos7 ~ ]# echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$1)'
2008-08-08 08:08:08
[root@centos7 ~ ]# echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$0);print $0}'
2008-08-08 08-08-08
split( s , array , [ r ]):以 r
为分隔符,切割字符串 s
,并将切割后的结果保存至 array
所表示的数组中,array数组第一个索引值为1,第二个索引值为2…
netstat -tn | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
awk '/test/{split($0,array,":");for(var in array){print var,array[var]}}' /etc/passwd
10.3 调用shell命令
system 函数:可以awk中调用shell命令 空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了 awk的变量外其他一律用""引用起来
awk 'BEGIN{system("hostname")}'
awk 'BEGIN{score=100; system("echo your score is " score) }'
面试题:找出当前连接数超过10次以上的IP,通过防火墙禁止访问
netstat -nt |awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){if(count[i]>=10){print i}}}'
面试题:查看NGINX访问日志,找出访问次数超过1000次的IP禁止访问
[root@centos7 ~ ]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log-20231014
[root@centos7 ~ ]# awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=10){system("iptables -A INPUT -s "i" -j REJECT")}}}' access_log-20231014
10.4 自定义函数
awk支持自定义函数,也支持向函数传参
awk函数语法
function 函数名(参数1,参数2.....){
函数体
return 返回值
}
awk函数调用
函数名(实参1,实参2,......)
示例
[root@centos7 ~ ]# cat func.awk
function compare(x,y){
var=x>y?x:y
return var
}
BEGIN{print compare(a,b)}
[root@centos7 ~ ]# awk -v a=10 -v b=30 -f func.awk
30
10.5 AWK脚本
示例:
[root@shell ~]# cat passwd.awk
{if($3>=100)print $1,$3}
[root@shell ~]# awk -F: -f passwd.awk /etc/passwd
systemd-network 192
polkitd 999
abrt 173
u2 998
nginx 997
示例:
[root@shell ~]# cat test.awk
#!/bin/awk -f
{if($3>=100)print $1,$3}
[root@shell ~]# chmod +x test.awk
[root@shell ~]# ./test.awk -F: /etc/passwd
systemd-network 192
polkitd 999
abrt 173
u2 998
nginx 997