awk
一· awk简介
awk是一门编程语言,用于处理文本,可以处理来自标准输入、或多个文件、或来自其他程序输出的文本。支持用户自定义函数和正则表达式。
处理文本的方式:他逐行扫描,寻找匹配正则表达式的行,并在行上执行指定操作,如果没有指定操作,则把所有匹配的行输出到屏幕。如果没有指定匹配模式则操作所有行。
二. awk的语法格式
awk [options] ‘command’ filenames
awk [options] -f awk-script-file filenames
三. awk的选项
-F : 定义输入字符分隔符,默认是空格或tab
四. awk的命令
BEGIN{} {} END{}
行处理前 行处理 行处理后
[root@localhost ~]# cat test.txt
111
222
333
[root@localhost ~]# awk 'BEGIN{print 1/2} {print "ok"} END{print "---"}' test.txt
0.5
ok
ok
ok
---
[root@localhost ~]#
BEGIN{}通常用于定义变量,如
BEGIN{FS=":";OFS="\t"}
五. awk的命令格式
awk ‘pattern’ filename
# 匹配pattern的行将整行输出,没匹配的行不输出
[root@localhost ~]# awk '/root/' passwd
root:x:0:0:root:/root:/bin/bash
awk ‘{action}’ filename
# 按照action处理所有行
[root@localhost ~]# awk -F":" '{print $1}' passwd
root
bin
daemon
awk ‘pattern {action}’ filename
# 只对匹配的行按照action处理
[root@localhost ~]# awk -F":" '/root/{print $2}' passwd
x
command | awk ‘pattern {action}’
[root@localhost ~]# df -P | grep '/' |awk '$4 > 8129256 {print $4}'
41888252
六. awk的工作原理
- awk用一行作为输入,并将这一行赋给内部变量$0, 每一行也可以称作一个记录,以换行符结束;
- 然后,行被(默认为空格或制表符)分解成字段,每个字段存储在已编号的变量中,从$1开始,多达100个字段;
- awk是如何知道用空格来分割字段呢?是因为有一个内部变量FS来确定字段分隔符,初始时FS为空格;
- awk打印字段时,将已设置的方式使用print函数打印,awk在打印的字段间加上空格,是因为$1和$2之间有逗号,逗号被映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格;
- awk输出之后,将从文件中获取下一行,并将其存储在变量$0中覆盖原来内容。
七. awk的内部变量
$0: 保存当前记录的内容
NR:记录的编号,如果同时处理多个文件,编号延续
FNR:记录的编号,如果同时处理多个文件,编号将重新开始
NF:记录中有几个字段
[root@localhost ~]# awk -F":" '{print $0,NF}' passwd
root:x:0:0:root:/root:/bin/bash 7
bin:x:1:1:bin:/bin:/sbin/nologin 7
daemon:x:2:2:daemon:/sbin:/sbin/nologin 7
FS:输入字段分隔符,默认为空格和tab
# 可以使用多个符号同时当做FS,利用用空格、tab和冒号
[root@localhost ~]# awk -F "[ \t:]" '{print $2}' test.txt
[root@localhost ~]# awk 'BEGIN{FS="[ \t:]"} {print $2}' test.txt
OFS:输出字段分隔符
RS:输入记录分隔符,默认为换行
[root@localhost ~]# cat test.txt
1:2:3:4 a:b:c:d
q:w:e:r 5:4:3:2
[root@localhost ~]# awk -F":" 'BEGIN{RS=" "} {print$1,$2}' test.txt
1 2
a b
5 4
ORS:输出记录分隔符,默认为换行
八. awk的两个打印函数print和printf
awk 'BEGIN{FS=":";print"开始了"} {print"用户名:" $1 "\t用户ID:" $3} END{print"结束了"}' /etc/passwd
- printf用于格式化输出
%s 字符类型
%d 数值类型
%f 浮点数值类型
15 占15个字符
- 表示左对齐,默认是右对齐
printf默认不会在行末打印换行,需要自己加\n
[root@localhost ~]# awk 'BEGIN{FS=":";print"开始了"} {printf"用户名:%-20sID:%-15s\n", $1,$3} END{print"结束了"}' /etc/passwd
开始了
用户名:root ID:0
用户名:bin ID:1
用户名:daemon ID:2
用户名:adm ID:3
用户名:lp ID:4
用户名:sync ID:5
用户名:shutdown ID:6
九 awk的command包括模式和动作两部分
# 任何awk语句都由模式和动作组成;
# 模式决定动作语句何时触发,以及触发事件;
# 动作是对数据进行的操作;
# 动作放着花括号中{}
# 如果省略模式部分,动作将保持时刻执行状态:
# 模式可以是任何条件语句或复合语句或正则表达式
# BEGIN和END是两个特色的模式
# BEGIN语句用于设置计数和打印头;BEGIN语句用于行数据处理之前
# END语句用于行数据处理之后,打印行总数和结尾状态
十 awk的模式
10.1 模式为正则表达式
# 整行匹配
## 在整行中匹配某个正则表达式时可以直接写正则表达式;也可以用$0~后再跟正则表达式
### 直接用正则表达式就是在整行做匹配
[root@localhost ~]# awk '/^andy/' /etc/passwd
andy:x:1000:1000:andy:/home/andy:/bin/bash
### 正则表达式前跟$0~也是在整行匹配正则表达式
[root@localhost ~]# awk '$0~/^andy/' /etc/passwd
andy:x:1000:1000:andy:/home/andy:/bin/bash
### 后面没有动作或动作为print$0都是整行打印的意思
[root@localhost ~]# awk '/^andy/{print $0}' /etc/passwd
andy:x:1000:1000:andy:/home/andy:/bin/bash
# !接正则表达式或!~接正则表达式都是取反的意思
[root@localhost ~]# awk '!/^andy/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@localhost ~]# awk '$0!~/^andy/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
# 字段匹配正则表达式
[root@localhost ~]# awk -F":" '$5~/andy/' /etc/passwd
andy:x:1000:1000:andy:/home/andy:/bin/bash
[root@localhost ~]# awk -F":" '$5!~/andy/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
10.2 模式为比较表达式
比较表达式对文本进行比较,只有当条件为真时,才执行指定的动作;
比较表达式使用关系运算符来比较数字或字符串。
关系运算符:
运算符 含义 示例
< 小于 a<b
<=
==
!=
>=
>
[root@localhost ~]# awk -F":" '$3>999' /etc/passwd
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
andy:x:1000:1000:andy:/home/andy:/bin/bash
[root@localhost ~]# awk -F":" '$3==0{print $1}' /etc/passwd
root
[root@localhost ~]# awk -F":" '$NF=="/bin/bash"{print $1}' /etc/passwd
root
andy
10.3 模式为条件表达式
awk ‘{if(条件表达式) {动作}}’
awk ‘{if(条件表达式) {动作1} else {动作2}}’
[root@localhost ~]# awk -F":" '$3==0 {print $1}' /etc/passwd
root
# 动作可以加花括号也可不加
[root@localhost ~]# awk -F":" '{if($3==0) {print $1}}' /etc/passwd
root
[root@localhost ~]# awk -F":" '{if($3==0) print $1}' /etc/passwd
root
10.4 模式中可以执行计算
算术表达式:±*/%^
awk都是按照浮点数方式来执行算术运算的
awk -F":" '$3*2>999' /etc/passwd
awk -F":" '{if($3*2>999){print $1}}' /etc/passwd
10.5 模式使用逻辑操作符和复合模式
&& 逻辑与 a&&b
|| 逻辑或 a||b
! 逻辑非 !a
[root@localhost ~]# awk -F":" '$7=="/bin/bash" && $3 > 999' /etc/passwd
andy:x:1000:1000:andy:/home/andy:/bin/bash
10.6 范围模式
awk范围模式先匹配从第一个模式的首次匹配的行到第二个模式的首次匹配的行之间的所有行;然后从下一行再次匹配从第一个模式正则表达式,如果匹配了就再匹配第二个模式,然后取这次两个匹配之间的所有行;以此类推。如果匹配到第一个模式而没有发现第二个模式, awk 就将显示从第一个模式首次出现的行到文件末尾之间的所有行。
[root@localhost ~]# awk -F":" '$1~/root/,$1~/lp/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
10.7 在比较表达式中,可以用.加数字表示零点几;在动作语句中可以把字段类型为数值类型的字段直接算术运算
# 将第二字段的值大于0.8的直接加10打印出来
[root@localhost ~]# cat test.txt
121 0.1 0.9 33
23 0.8 2323 34
23 0.9 sdf sdf
[root@localhost ~]# awk '$2>.8{print $2+10}' test.txt
10.9
[root@localhost ~]# awk '$2>.8{print $2-.2}' test.txt
0.7
10.8 动作中可以使用三目运算表达式来代替if else
三目运算表达式: a?b:c
如果条件表达式a成立,则三目运算表达式的值为b;
如果条件表达式a不成立,则三目运算表达式的值为c
[root@localhost ~]# cat test.txt
121 0.1 0.9 33
23 0.8 2323 34
23 0.9 sdf sdf
[root@localhost ~]# awk '{print ($1>23? "第一个字段的值大于23:"$1 : "第一个字段的值小于23:"$1)}' test.txt
第一个字段的值大于23:121
第一个字段的值小于23:23
第一个字段的值小于23:23
[root@localhost ~]# awk '{if($1>23){print "第一个字段的值大于23:"$1}else{print "第一个字段的值小于23:"$1}}' test.txt
第一个字段的值大于23:121
第一个字段的值小于23:23
第一个字段的值小于23:23
10.9 动作中可以用赋值运算符来修改原数据
[root@localhost ~]# cat test.txt
121 0.1 0.9 33
23 0.8 2323 34
23 0.9 sdf sdf
[root@localhost ~]# awk '{$1="andy" ; print$0}' test.txt
andy 0.1 0.9 33
andy 0.8 2323 34
andy 0.9 sdf sdf
[root@localhost ~]# awk '{$1-=100 ; print$0}' test.txt
21 0.1 0.9 33
-77 0.8 2323 34
-77 0.9 sdf sdf
十一 awk的脚本编程
11.1 条件判断
11.1.1 if语句
格式:{if(表达式) {语句;语句;…}}
[root@localhost ~]# ss -an |awk '{if($2=="LISTEN"){count++;print$2}} END{print count}'
LISTEN
LISTEN
2
1.1.2 if…else语句
格式:{if(表达式) {语句;语句;…} else{语句;语句;…}}
[root@localhost ~]# ss -an |awk '{if($2=="LISTEN"){l++} else{o++}} END{print "监听端口个数:" l ; print "其他端口个数:" o}'
监听端口个数:41
其他端口个数:248
11.1.3 if…else if…else语句
格式:{if(表达式1) {语句;语句;…} else if(表达式2) {语句;语句;…} else{语句;语句;…}}
[root@localhost ~]# ss -an |awk '{if($2=="LISTEN"){l++} else if($2=="ESTAB"){e++} else{o++}} END{print "监听端口个数:" l ; print "ESTAB端口个数:" e ; print "其他端口个数:" o}'
监听端口个数:41
ESTAB端口个数:139
其他端口个数:109
11.2 循环
11.2.1 while
格式:i=1; while(i<5){语句1;语句2;i++}
[root@localhost ~]# cat test.txt
121 0.1 0.9
23 0.8 232
[root@localhost ~]# awk '{i=1; while(i<=NF){$i+=100;print$i;i++}}' test.txt
221
100.1
100.9
123
100.8
332
[root@localhost ~]# awk '{i=1; while(i<=NF){$i+=100;i++}; {print$0}}' test.txt
221 100.1 100.9
123 100.8 332
11.2.2 for
格式: for(i=1;i<5;i++){语句1;语句2}
[root@localhost ~]# cat test.txt
121 0.1 0.9
23 0.8 232
[root@localhost ~]# awk '{for(i=1;i<3;i++){print$0}}' test.txt
121 0.1 0.9
121 0.1 0.9
23 0.8 232
23 0.8 232
十二 awk的数组
12.1 awk的不分普通数组和关联数组
[root@localhost ~]# awk 'BEGIN{FS=":"} {lst1[++i]=$1} END{print lst1[1]}' /etc/passwd
root
[root@localhost ~]# ss -ant |awk '{lst1[$1]++} END{for (i in lst1){print i,lst1[i]}}'
LISTEN 7
ESTAB 1
State 1
12.2 awk数组的遍历-用索引便利
for (i in 数组){print i,数组[i]}
其中i是索引;数组[i]是值
12.3 使用awk数组统计数据的使用原则
将需要统计的数据(某一字段)作为数组的索引去++
十三 awk的内置函数
13.1 length函数计算字符串的长度
[root@DTOS data]# awk -F":" 'length($1)==4 {print$1;count++} END{print "count is:",count}' /etc/passwd
root
sync
halt
mail
dbus
sshd
count is: 6
13.2 sub函数是字符串替换函数
[root@DTOS data]# echo "zhang_andy_zhang" |awk 'sub(/zhang/,"wang")'
wang_andy_zhang
13.3 gsub函数是字符串全局替换函数
[root@DTOS data]# echo "zhang_andy_zhang" |awk 'gsub(/zhang/,"wang")'
wang_andy_wang
13.4 int函数是去一个字符串最前面的整数部分
[root@localhost ~]# var1=3%; var2=3.3%; var3=3test; var4=%3
[root@localhost ~]# awk "BEGIN{print int(\"$var1\")}"
3
[root@localhost ~]# awk "BEGIN{print int(\"$var2\")}"
3
[root@localhost ~]# awk "BEGIN{print int(\"$var3\")}"
3
[root@localhost ~]# awk "BEGIN{print int(\"$var4\")}"
0
十四 awk使用变量
14.1 使用外部变量
14.1.1 在双引号下使用
awk "函数(\"$变量名\")")
[root@DTOS data]# var="zhang"
[root@DTOS data]# echo "wang tony" | awk "sub(/wang/,$var)"
tony
[root@DTOS data]# echo "wang tony" | awk "sub(/wang/,"$var")"
tony
[root@DTOS data]# echo "wang tony" | awk "sub(/wang/,'$var')"
awk: cmd. line:1: sub(/wang/,'zhang')
awk: cmd. line:1: ^ invalid char ''' in expression
awk: cmd. line:1: sub(/wang/,'zhang')
awk: cmd. line:1: ^ syntax error
[root@DTOS data]# echo "wang tony" | awk "sub(/wang/,\'$var\')"
awk: cmd. line:1: sub(/wang/,\'zhang\')
awk: cmd. line:1: ^ backslash not last character on line
awk: cmd. line:1: sub(/wang/,\'zhang\')
awk: cmd. line:1: ^ syntax error
[root@DTOS data]# echo "wang tony" | awk "sub(/wang/,\"$var\")"
zhang tony
awk "{\"$变量名\"}"
[root@DTOS data]# var="zhang"
[root@DTOS data]# echo "wang tony" | awk "{print \"$var\"}"
zhang
14.1.2 在单引号下使用
“’”$变量名"’"
[root@DTOS data]# var="zhang"
[root@DTOS data]# echo "wang tony" | awk 'sub(/wang/,"'"$var"'")'
zhang tony
awk ‘{"’"$变量名"’"}’
[root@DTOS data]# var="zhang"
[root@DTOS data]# echo "wang tony" | awk '{print "'"$var"'"}'
zhang
14.2 使用awk内用v参数定义的变量
[root@andy1 grub2]# echo "zhang andy zhang" |awk -v var1="wang" '{print var1}'
wang
[root@andy1 grub2]# echo "zhang andy zhang" |awk -v var1="wang" '{sub(/zhang/,var1);print$0}'
wang andy zhang
十五 将awk输出的字符串作为一个命令让bash执行
15.1 方法一
用管道交给bash
[root@localhost ~]# arp -n |awk '/^[0-9]/{print "arp -d "$1}' |bash
15.2 方法二
把awk的输出作为参数交给xargs后的命令处理
[root@localhost ~]# arp -n |awk '/^[0-9]/{print $1}' |xargs -t -n1 arp -d
arp -d 192.168.1.58
arp -d 192.168.1.217
arp -d 192.168.1.100
arp -d 192.168.1.251
arp -d 192.168.1.235
arp -d 192.168.1.241
或
[root@localhost ~]# arp -n |awk '/^[0-9]/{print $1}' |xargs -t -I @ arp -d @
arp -d 192.168.1.58
arp -d 192.168.1.100
arp -d 192.168.1.251
十六 用rand函数取到随机值
rand()函数是随机产生一个0到1之间的保留小数点后6位的小数值,例如0.217788;
想得到一个1到7之间整数的一个随机值需要乘以100得到21.7788,然后再对7取余,结果是0.7788,int()取整是0了,我们要获得1~7的随机数,所以加1,整个表达式才是 int(rand()*100%7+1)
[root@DTOS ~]# awk '{print int(rand()*100%7+1)}'
3
2
1
2
3
6