文本三剑客之AWK

一.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}’
FNRawk命令默认接收多个文件连续按每一行进行编号,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"

注意:

  1. awk数组为关联数组,实现key / vaule功能
  2. 可使用任意字符串;字符串要使用双引号括起来
  3. 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
  4. 若要判断数组中是否存在某元素,要使用“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
  • 47
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值