linux|shell编程--三剑客之awk

一、awk基础语法(常用,其他不常用) 语法:

awk[选项]‘条件{动作}条件{动作}’文件名 ...

1、内置变量
变量名 描述
FILENAME 当前输入文件的名称
FNR 当前输入文档的当前行号,尤其当有多个输入文档时有用
NR 输入数据流的当前行号
$0 当前行的全部内容
$n 当前行的第n个字段内容(n>1)
NF 当前记录(行)的字段(列)个数
FS 字段分隔符,默认为空格或Tab制表符
OFS 输出字段分隔符,默认为空格
ORS 输出行分隔符,默认换行符为\n
RS 输入行分隔符,默认换行符为\n
[root@www jiaofan]# free | awk '{print $2}'#<==打印第二列 used 995672 2097148 [root@www jiaofan]# free | awk '{print NR}'#<==输出行号 1 2 3 [root@www jiaofan]# free | awk '{print NF}'#<==输出每行的列数 6 7 4 [root@www jiaofan]# free | awk '{print $NF}'#<==打印最后一列 available 287716 2097148 [root@www jiaofan]# free | awk '{print $(NF-1)}'#<==打印倒数第二列 buff/cache 288292 0

[root@www jiaofan]# cat test1.txt#<==文件1 hello world! other men live to eat,while I eat to live. It is never too late to mend. [root@www jiaofan]# cat test2.txt#<==文件2 biscuit:crisp:chichen salt-jam:oil,suger banana:lemon,pear--apple:grape [root@www jiaofan]# awk '{print $2}' test1.txt#<==输出第一列 world! men is [root@www jiaofan]# awk '{print $0}' test1.txt#<==输出每行的全部内容 hello world! other men live to eat,while I eat to live. It is never too late to mend. [root@www jiaofan]# awk '{print}' test1.txt#<==输出每行的全部内容 hello world! other men live to eat,while I eat to live. It is never too late to mend. [root@www jiaofan]# awk '{print NR}' test1.txt test2.txt#<==打印当前行号 1 2 3 4 5 6 [root@www jiaofan]# awk '{print FNR}' test1.txt test2.txt#<==打印当前文件行号 1 2 3 1 2 3 [root@www jiaofan]# awk'{print FILENAME}'test1.txt test2.txt test1.txt test1.txt test1.txt test2.txt test2.txt test2.txt [root@www jiaofan]#

2、自定义变量(-v)
[root@www jiaofan]# awk -v x="jiaofan"-v y=11'{print x,y}' test1.txt jiaofan 11 jiaofan 11 jiaofan 11 [root@www jiaofan]# x="hello" [root@www jiaofan]# awk -v i=$x '{print i}' test1.txt#<==输出变量 hello hello hello [root@www jiaofan]# awk '{print "'$x'"}' test1.txt#<==输出变量 hello hello hello

定义分隔符:FS 或 F
[root@www jiaofan]# cat test2.txt biscuit:crisp:chichen salt-jam:oil,suger banana:lemon,pear--apple:grape [root@www jiaofan]# awk -v FS=":" '{print $1}' test2.txt#<==以:为分隔符 biscuit salt-jam banana [root@www jiaofan]# awk -v FS="[:,-]"'{print $1}' test2.txt#<==以:和-为分隔符 biscuit salt banana [root@www jiaofan]# awk -F":" '{print $1}' test2.txt#<==以:为分隔符 biscuit salt-jam banana [root@www jiaofan]# awk -F"[:,-]"'{print $1}' test2.txt#<==以:和-为分隔符 biscuit salt banana [root@www jiaofan]#

输入行分隔符,默认是\n,可以修改RS自定义分隔符
[root@www jiaofan]# cat test1.txt hello world! other men live to eat,while I eat to live. It is never too late to mend. [root@www jiaofan]# awk -v RS="," '{print $1}' test1.txt hello while

上面以逗号问行分隔符,所以输入内容为两行分别是
hello world!\nother men live to eat
while I eat to live.\nIt is never too late to mend.
输出字段分隔符OFS
[root@www jiaofan]# awk '{print $3,$1,$3}' test1.txt#<==默认是空格为分隔符 hello live other live never It never [root@www jiaofan]# awk -v OFS=":" '{print $3,$1,$3}' test1.txt#<==设置冒号为分隔符 :hello: live:other:live never:It:never [root@www jiaofan]# awk -v OFS="-" '{print $3,$1,$3}' test1.txt -hello- live-other-live never-It-never [root@www jiaofan]# awk -v OFS="\t" '{print $3,$1,$3}' test1.txt#<==设置制表符为分隔符 hello live other live never It never [root@www jiaofan]# awk -v OFS="." '{print NR,$0}' test1.txt 1.hello world! 2.other men live to eat,while I eat to live. 3.It is never too late to mend.

输出行分隔符ORS
[root@www jiaofan]# cat test1.txt hello world! other men live to eat,while I eat to live. It is never too late to mend. [root@www jiaofan]# awk -v ORS=":" '{print}'test1.txt hello world!:other men live to eat,while I eat to live.:It is never too late to mend.:

3、print 指令
[root@www jiaofan]# awk '{print "data:",$1}' test1.txt data: hello data: other data: It [root@www jiaofan]# awk '{print 12345}' test1.txt 12345 12345 12345 [root@www jiaofan]# awk '{print $1,12345,$3}' test1.txt hello 12345 other 12345 live It 12345 never [root@www jiaofan]# awk '{print "第一列",$1,"\t第二列:",$2}' test1.txt 第一列 hello第二列: world! 第一列 other第二列: men 第一列 It第二列: is

4、条件匹配
比较符号 描述
// 全行数据正则匹配
!// 对全行数据正则匹配后取反
~// 对特定数据正则匹配
!~// 对特定数据正则匹配后取反
== 等于
!= 不等于
> 大于
>= 大于等于
< 小于
<= 小于等于
&& 逻辑与,如A&&B,要求满足A并且满足B
|| 逻辑或,如A||B,要求满足A或者满足B
[root@www jiaofan]# cat test1.txt#<==准备素材 hello the world! other men live to eat,while I eat to live. It is never too late to mend. [root@www jiaofan]# awk '/the/'test1.txt#<==每行正则匹配the hello the world! other men live to eat,while I eat to live. [root@www jiaofan]# awk '$2~/the/'test1.txt#<==每行第二列正则匹配the hello the world! [root@www jiaofan]# awk '$4==to'test1.txt#<==每行第四列精确匹配the hello the world! [root@www jiaofan]# awk -F: '$3>=100' /etc/passwd systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin nisuser1:x:1001:1001::/home/nisuser1:/bin/bash nisuser2:x:1002:1002::/home/nisuser2:/bin/bash nisuser3:x:1003:1003::/home/nisuser3:/bin/bash [root@www jiaofan]# awk 'NR==4' /etc/passwd adm:x:3:4:adm:/var/adm:/sbin/nologin [root@www jiaofan]# awk -F: '$3>1 && $3<5' /etc/passwd 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 [root@www jiaofan]# awk -F: '$3==1 || $3==5' /etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync [root@www jiaofan]#

awk 的匹配条件可以使BEGIN 或 END(大写字母),BEGIN 会导致动作指令仅在读取任何数据记录之前执行一次,END 会导致动作指令仅在读取完所有数据记录后执行一次。利用 BEGIN 我们可以进行数据的初始化操作,而 END 则可以帮助我们进行数据的汇总操作。
[root@www jiaofan]# awk -F:'BEGIN{print "用户名 UID 解释器"}{print $1,$3,$7}END{print "总计有"NR"个账户。"}'/etc/passwd | column -t 用户名UID解释器 root0/bin/bash bin1/sbin/nologin daemon2/sbin/nologin ... ... 总计有25个账户。 [root@www jiaofan]# awk 'BEGIN{print 10-4}' 6 [root@www jiaofan]# awk 'BEGIN{x=8; y=2; print x-y}'#<==对变量进行减法 6

在awk中变量不需要定义就可以直接使用,作为字符处理时未定义的变量默认值为空,作为数字处理时未定义的变量默认值为0
[root@www jiaofan]# awk 'BEGIN{print x+8}' 8 [root@www jiaofan]# awk 'BEGIN{print "["x"]","["y"]"}' [] [] [root@www jiaofan]# who | awk '$1=="root"{x++} END{print x}' 2 [root@www jiaofan]# ls -l /etc/*.conf | awk '{sum+=$5} END{print sum}' 44107 [root@www jiaofan]# ls -l /etc/*.conf | awk '/^-/{sum+=$5} END{print "文件总量:"sum"."}' 文件总量:44107. [root@www jiaofan]#

二、条件判断 awk单支语句
if(判断条件){ 动作指令序列; }

awk双支语句
if(判断条件){ 动作序列1; } else{ 动作序列2; }

awk多支语句
if(判断条件1){ 动作序列1; } else if(判断条件2){ 动作序列1; } else{ 动作序列2; }

三、awk 数组
一维数组: 数据名[索引]=值 多维数组: 数据名[索引1][索引2]=值 或者 数据名[索引1,索引2]=值

【linux|shell编程--三剑客之awk】for循环读取索引
for(变量 in 数据名){ 动作指令序列 }

判断索引是否为数组成员
if (索引 in 数据)

四、awk 循环 for循环
for(表达式1; 表达式2; 表达式3){ 动作指令序列 }

while 循环
while(条件判断){ 动作指令序列; }

五、awk 函数 1、内置I/O函数
函数 说明
getline getline会读取下一条数据,而不会影响后续awk指令的执行
next next不仅读取下一行数据,会导致后续的指令不再执行,而是重新读取数据后重新回到awk指令的开始位置,重新匹配,重新执行动作指令
system(“命令”) awk会启动一个新的shell进程执行命令
[root@www jiaofan]# cat test.txt 1:hello the world. 2:go spurs air. 3:123 456 789. 4:hello the beijing. [root@www jiaofan]# awk '/air/{getline; print "next line:",NR,$0} {print "noraml line NR="NR}' test.txt noraml line NR=1 next line: 3 3:123 456 789. noraml line NR=3 noraml line NR=4 [root@www jiaofan]# awk '/air/{next; print "next line:",$0} {print "noraml line NR="NR}' test.txt noraml line NR=1 noraml line NR=3 noraml line NR=4

[root@www jiaofan]# awk 'BEGIN{system("uptime")}' 16:44:45 up6:56,3 users,load average: 0.00, 0.01, 0.05 [root@www jiaofan]# awk 'BEGIN{system("echo jiaofan")}' jiaofan

2、内置数值函数
函数 说明
cos(expr) 函数返回expr的cosine值
sin(expr) 函数返回expr的sine值
sqrt(expr) 函数返回expr的平方
int(expr) 函数为取整函数,仅截取整数部分的数值
rand() 函数可以返回0到1之间的随机数N(0<=N<1)
srand() 函数可以使用expr定义新的随机数种子,没有expr时则使用当前系统的时间为随机数种子
[root@www jiaofan]# awk 'BEGIN{print cos(50)}' 0.964966 [root@www jiaofan]# awk 'BEGIN{print sin(50)}' -0.262375 [root@www jiaofan]# awk 'BEGIN{print sqrt(2)}' 1.41421 [root@www jiaofan]# awk 'BEGIN{print sqrt(4)}' 2 [root@www jiaofan]# awk 'BEGIN{print int(4.6)}' 4 [root@www jiaofan]# awk 'BEGIN{print rand()}' 0.237788 [root@www jiaofan]# awk 'BEGIN{for(i=1; i<=5; i++)print rand()}' 0.237788 0.291066 0.845814 0.152208 0.585537 [root@www jiaofan]# awk 'BEGIN{print 100*rand()}' 23.7788 [root@www jiaofan]# awk 'BEGIN{print rand()}'#<==没有新种子随机数固定 0.237788 [root@www jiaofan]# awk 'BEGIN{srand(); print rand()}'#<==使用时间做随机数 0.217179 [root@www jiaofan]# awk 'BEGIN{srand(897); print rand()}'#<==使用897做随机数 0.504158 [root@www jiaofan]# awk 'BEGIN{srand(897); print rand()}' 0.504158

3、内置字符串函数 length([s]) 函数可以统计字符串s的长度,如果不指定字符串 s 则统计$0的长度。
[root@www jiaofan]# awk 'BEGIN{test="hello the world"; print length(test)}' 15

index(字符串1,字符串2) 函数返回字符串2在字符串1中的位置坐标。
[root@www jiaofan]# awk 'BEGIN{test="hello the world"; print index(test,"h")}'#<==h在test变量的第一个位置 1 [root@www jiaofan]# awk 'BEGIN{test="hello the world"; print index(test,"t")}'#<==t在test变量的第一个位置 7

match(s,r) 函数根据正则表达式 r 返回其在字符串s中的位置坐标。
[root@www jiaofan]# awk 'BEGIN{print match("How much? 981$","[0-9]")}'#<==数字在第11个位置出现 11 [root@www jiaofan]# awk 'BEGIN{print match("How 4much? 981$","[0-9]")}'#<==数字在第5个位置出现 5 [root@www jiaofan]# awk 'BEGIN{print match("How much? 981$","[a-z]")}' 2 [root@www jiaofan]# awk 'BEGIN{print match("How much? 981$","[A-Z]")}' 1

tolower(str) 函数可以将字符串转换为小写
[root@www jiaofan]# awk 'BEGIN{apple[0]="red aPPLe"; print tolower(apple[0])}' red apple

toupper(str) 函数可以将字符串转换为大写。
[root@www jiaofan]# awk 'BEGIN{apple[0]="red aPPLe"; print toupper(apple[0])}' RED APPLE

split(字符串,数组,分隔符) 函数可以将字符串按特定的分隔符切片后存储在数组中,如果没有指定分隔符,则使用FS定义的分隔符进行字符串分隔。
[root@www jiaofan]# awk 'BEGIN{split("hello the world",test); print test[1],test[2]}' hello the

这条命令以空格或Tab键为分隔符,将hello the world 切割为独立的三个部分,分别存入 test[1]、test[2]、test[3] 数组中,最后通过 print 指令可以按照任意顺序打印显示这些数组元素的值。
[root@www jiaofan]# awk 'BEGIN{split("hello:the:world",test); print test[1] test[2]}'#<==test[2]为空值 hello:the:world [root@www jiaofan]# awk 'BEGIN{split("hello:the:world",test,":"); print test[1] test[2]}'#<==自定义分隔符为冒号 hellothe [root@www jiaofan]# awk 'BEGIN{split("hello:the:world",test,":"); print test[1],test[2]}' hello the [root@www jiaofan]# awk 'BEGIN{split("hi8the3world4!",test,"[0-9]"); print test[1],test[4]}'#<==使用正则定义分隔符 hi ! [root@www jiaofan]#

gsub(r,s,[,t]) 函数可以将字符串 t 中所有的正则表达式 r 匹配的字符串全部替换为 s ,如果没有指定字符串 t ,默认对$0进行替换操作。
[root@www jiaofan]# awk 'BEGIN{hi="hello world"; gsub("o","O",hi); print hi}'#<==小写o换成大写O hellO wOrld [root@www jiaofan]# head -1 /etc/passwd | awk '{gsub("[0-9]","**"); print $0}'#<==全部的数字替换成** root:x:**:**:root:/root:/bin/bash

sub(r,s,[,t]) 函数与gsub类似,但仅替换第一个匹配的字符串,而不是替换全部。
[root@www jiaofan]# head -1 /etc/passwd | awk '{sub("[0-9]","**"); print $0}' root:x:**:0:root:/root:/bin/bash [root@www jiaofan]# head -1 /etc/passwd | awk '{sub("root","**"); print $0}' **:x:0:0:root:/root:/bin/bash

substr(s,i[,n]) 函数可以对字符串s 进行截取,从第i位开始,截取n个字符串,如果n没有指定则一直截取到字符串s的末尾位置。
[root@www jiaofan]# awk 'BEGIN{hi="hello world"; print substr(hi,2,3)}'#<==从第2位开始截取3个字符 ell [root@www jiaofan]# awk 'BEGIN{hi="hello world"; print substr(hi,2)}'#<==从第2位开始截取到末尾 ello world

4、内置时间函数 systime() 返回当前时间距离1970-01-01 00:00:00 有多少秒。
[root@www jiaofan]# awk 'BEGIN{print systime()}' 1646017041 [root@www jiaofan]#

    推荐阅读