使用方法

awk '{pattern + action}' {filenames}
其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。 pattern就是要表示的正则表达式,用斜杠括起来。

awk语言的最基本功能是在文件或者字符串中基于指定规则浏览和抽取信息,awk抽取信息后,才能进行其他文本操作。完整的awk脚本通常用来格式化文本文件中的信息。

通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。

awk 的原理

  1. [root@localhost ~]# awk '{print $0}' /etc/passwd

执行 awk 时,它依次对/etc/passwd 中的每一行执行 print 命令。

Linux三剑客之awk - 图1

  1. [root@localhost ~]# awk -F":" '{print $1}' /etc/passwd
  2. [root@localhost ~]# awk -F":" '{print $1 $3}' /etc/passwd
  3. [root@localhost ~]# awk -F":" '{print $1" " $3}' /etc/passwd
  4. [root@localhost ~]# awk -F":" '{print "username:"$1"\t\tuid:" $3}' /etc/passwd

-F参数:指定分隔符,可指定一个或多个
print 后面做字符串的拼接

实例一:只查看test.txt文件(100行)内第20到第30行的内容(企业面试)

  1. [root@localhost ~]# awk '{if(NR>=20 && NR<=30) print $1}' test.txt
  2. 20
  3. 21
  4. 22
  5. 23
  6. 24
  7. 25
  8. 26
  9. 27
  10. 28
  11. 29
  12. 30

实例二:已知test.txt文件内容为

  1. [root@localhost ~]# cat test.txt
  2. I am aaron, my qq is 1234567

请从该文件中过滤出’aaron’字符串与1234567,最后输出的结果为:aaron 1234567

  1. [root@localhost ~]# awk -F '[ ,]+' '{print $3" "$7}' test.txt
  2. aaron 1234567

BEGIN 和 END 模块

实例一:统计/etc/passwd的账户人数

  1. [root@localhost ~]# awk '{count++;print $0;} END{print "user count is ",count}' /etc/passwd

count是自定义变量。之前的action{}里都是只有一个print,其实print只是一个语句,而action{}可以有多个语句,以;号隔开。这里没有初始化count,虽然默认是0,但是妥当的做法还是初始化为0

  1. awk 'BEGIN {count=0;print "[start] user count is ",count}{count++;print $0} END{print "[end] user count is ",count}' /etc/passwd

实例二:统计某个文件夹下的文件占用的字节数

  1. [root@localhost ~]# ll | awk 'BEGIN {size=0} {size=size+$5} END{print "size is ",size}'
  2. size is 1468
  1. [root@localhost ~]# ll | awk 'BEGIN {size=0} {size=size+$5} END{print "size is ",size/1024/1024,"M"}'
  2. size is 0.00139999 M

awk运算符

运算符 描述
赋值运算符
= += -= = /= %= ^= *= 赋值语句
逻辑运算符
|| 逻辑或
&& 逻辑与
正则运算符
~ !~ 匹配正则表达式和不匹配正则表达式
关系运算符
< <= > >= != == 关系运算符
算数运算符
+ - 加,减
* / & 乘,除与求余
+ - ! 一元加,减和逻辑非
^ * 求幂
++ — 增加或减少,作为前缀或后缀
其他运算符
$ 字段引用
空格 字符串链接符
?: 三目运算符
ln 数组中是否存在某键值

awk 赋值运算符:a+5;等价于: a=a+5;其他同类

  1. [root@node-1 ~]# awk 'BEGIN{a=5;a+=5;print a}'
  2. 10

awk逻辑运算符:判断表达式 a>2&&b>1为真还是为假,后面的表达式同理

  1. [root@node-1 ~]# awk 'BEGIN{a=1;b=2;print (a>2&&b>1,a=1||b>1)}'
  2. 0 1

awk正则运算符:

  1. [root@node-1 ~]# awk 'BEGIN{a="100testaa";if(a~/100/) {print "OK"}}'
  2. OK
  3. [root@node-1 ~]# echo | awk 'BEGIN{a="100testaa"}a~/100/{print "OK"}'
  4. OK

关系运算符:
如: > < 可以作为字符串比较,也可以用作数值比较,关键看操作数如果是字符串就会转换为字符串比较。两个都为数字 才转为数值比较。字符串比较:按照ascii码顺序比较。

  1. [root@node-1 ~]# awk 'BEGIN{a="11";if(a>=9){print"OK"}}'
  2. [root@node-1 ~]# awk 'BEGIN{a=11;if(a>=9){print"OK"}}'
  3. OK
  4. [root@node-1 ~]# awk 'BEGIN{a;if(a>=b){print"OK"}}'
  5. OK

awk 算术运算符:
说明,所有用作算术运算符进行操作,操作数自动转为数值,所有非数值都变为0。

  1. [root@node-1 ~]# awk 'BEGIN{a="b";print a++,++a}'
  2. 0 2
  3. [root@node-1 ~]# awk 'BEGIN{a="20b4";print a++,++a}'
  4. 20 22

这里的a++ , ++a与javascript语言一样:a++是先赋值加++;++a是先++再赋值

三目运算符 ?:

  1. [root@node-1 ~]# awk 'BEGIN{a="b";print a=="b"?"ok":"err"}'
  2. ok
  3. [root@node-1 ~]# awk 'BEGIN{a="b";print a=="c"?"ok":"err"}'
  4. err

常用 awk 内置变量

变量名 属性
$0 当前记录
$1~$n 当前记录的第n个字段
FS 输入字段分割符 默认是空格
RS 输入记录分割符 默认为换行符
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始
OFS 输出字段分割符 默认也是空格
ORS 输出的记录分割符 默认为换行符

注:内置变量很多,参阅相关资料
字段分隔符 FS
FS=”\t” 一个或多个 Tab 分隔

  1. [root@node-1 ~]# cat tab.txt
  2. aa bb cc
  3. [root@node-1 ~]# awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' tab.txt
  4. aa bb cc

FS=”[[:space:]+]” 一个或多个空白空格,默认的,匹配到不符合的就停止

  1. [root@node-1 ~]# awk -F [[:space:]+] '{print $1,$2,$3,$4,$5}' tab.txt
  2. aa bb cc
  3. [root@node-1 ~]# awk -F [[:space:]+] '{print $1,$2}' tab.txt
  4. aa bb

FS=”[“ “:]+” 以一个或多个空格或:分隔

  1. [root@node-1 ~]# awk -F [" ":]+ '{print $1,$2,$3}' hello.txt
  2. root x 0

字段数量 NF :显示满足用:分割,并且有8个字段的

  1. [root@node-1 ~]# awk -F ":" 'NF==8{print $0}' hello.txt
  2. bin:x:1:1:bin:/bin:/sbin/nologin:888

记录数量 NR

  1. [root@node-1 ~]# ifconfig br0 | awk -F [" ":]+ 'NR==2{print $3}'
  2. 192.168.0.241

RS 记录分隔符变量
将 FS 设置成”\n”告诉 awk 每个字段都占据一行。通过将 RS 设置成””,还会告诉 awk每个地址记录都由空白行分隔。

  1. [root@node-1 ~]# cat awk.txt
  2. #!/bin/awk
  3. BEGIN {
  4. FS="\n"
  5. RS=""
  6. }
  7. {
  8. print $1","$2","$3
  9. }
  10. [root@node-1 ~]# awk -f awk.txt recode.txt

在””分割符之内,符合\n分割的会被打印出来
OFS 输出字段分隔符

  1. [root@node-1 ~]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' hello.txt
  2. root#x#0
  3. bin#x#1

ORS 输出记录分隔符

  1. [root@node-1 ~]# cat awk.txt
  2. #!/bin/awk
  3. BEGIN {
  4. FS="\n"
  5. RS=""
  6. ORS="\n\n"
  7. }
  8. {
  9. print $1","$2","$3
  10. }
  11. [root@node-1 ~]# awk -f awk.txt recode.txt
  12. Jimmy the Weasel,100 Pleasant Drive,San Francisco,CA 123456
  13. Big Tony,200 Incognito Ave.,Suburbia,WA 64890

awk正则

元字符 功能 示例 解释
^ 首航定位符 /^root/ 匹配所有以root开头的行
$ 行尾定位符 /root$/ 匹配所有以root结尾的行
. 匹配任意单个字符 /r..t/ 匹配字母r,然后两个任意字符,再以t结尾的行
* 匹配0个或多个前导字符(包括回车) /a*ool/ 匹配0个或多个a之后紧跟着ool的行,比如ool,aaaaool等
+ 匹配1个或多个前导字符 /a+b/ ab, aaab
? 匹配0个或1个前导字符 /a?b/ b,ab
[] 匹配指定字符组内的任意一个字符 /^[abc]/ 匹配以a或b或c开头的行
[^] 匹配不在指定字符组内任意一个字符 /^[^abc]/ 匹配不以字母a或b或c开头的行
() 子表达式组合 /(rool)+/ 表示一个或多个rool组合,当有一些字符需要组合时,使用括号括起来
| 或者的意思 /(root)|B/ 匹配root或者B的行
\ 转义字符 /a\/\// 匹配a//
~,!~ 匹配,不匹配的条件语句 $1~/root/ 匹配第一个字段包含字符root的所有记录
x{m}
x{m,}
x{m,n}
x重复m次
x重复至少m次
x重复至少m次,但是不超过n次
/(root){3}/
/(root){3,}/
/(root){3,6}/

正则应用
规则表达式
awk '/REG/{action} ' file ,/REG/为正则表达式,可以将$0 中,满足条件的记录送入到:action 进行处理

  1. [root@node-1 ~]# awk '/root/{print$0}' /etc/passwd
  2. root:x:0:0:root:/root:/bin/bash
  3. operator:x:11:0:operator:/root:/sbin/nologin
  4. [root@node-1 ~]# awk -F ":" '$5~/root/{print$0}' /etc/passwd
  5. root:x:0:0:root:/root:/bin/bash
  6. [root@node-1 ~]# ifconfig br0 | awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print$3}'
  7. 192.168.0.241

布尔表达式
awk '布尔表达式{action}' file 仅当对前面的布尔表达式求值为真时, awk 才执行代码块。

  1. [root@node-1 ~]# awk -F: '$1=="root"{print$0}' /etc/passwd
  2. root:x:0:0:root:/root:/bin/bash
  3. [root@node-1 ~]# awk -F: '($1=="root")&&($5=="root"){print$0}' /etc/passwd
  4. root:x:0:0:root:/root:/bin/bash

awk 的 if、循环和数组

if

条件语句
awk 提供了非常好的类似于 C 语言的 if 语句。

  1. {
  2. if ($1=="foo"){
  3. if ($2=="foo"){
  4. print"uno"
  5. }else{
  6. print"one"
  7. }
  8. }elseif($1=="bar"){
  9. print "two"
  10. }else{
  11. print"three"
  12. }
  13. }

使用 if 语句还可以将代码:

  1. ! /matchme/ { print $1 $3 $4 }

转换成:

  1. {
  2.   if ( $0 !~ /matchme/ ) {
  3.     print $1 $3 $4
  4.   }
  5. }

while

循环结构
我们已经看到了 awk 的 while 循环结构,它等同于相应的 C 语言 while 循环。 awk 还有”do…while”循环,它在代码块结尾处对条件求值,而不像标准 while 循环那样在开始处求值。
它类似于其它语言中的”repeat…until”循环。以下是一个示例:
do…while 示例

  1. {
  2. count=1do {
  3. print "I get printed at least once no matter what"
  4. } while ( count !=1 )
  5. }

与一般的 while 循环不同,由于在代码块之后对条件求值, “do…while”循环永远都至少执行一次。换句话说,当第一次遇到普通 while 循环时,如果条件为假,将永远不执行该循环。

for 循环

awk 允许创建 for 循环,它就象 while 循环,也等同于 C 语言的 for 循环:

  1. for ( initial assignment; comparison; increment ) {
  2. code block
  3. }

以下是一个简短示例:

  1. for ( x=1;x<=4;x++ ) {
  2. print "iteration", x
  3. }

break 和 continue
此外,如同 C 语言一样, awk 提供了 break 和 continue 语句。使用这些语句可以更好地控制 awk 的循环结构。

  1. #!/bin/awk
  2. BEGIN{
  3. x=1
  4. while(1) {
  5. print "iteration",x
  6. if ( x==10 ){
  7. break
  8. }
  9. x++
  10. }
  11. }

continue 语句补充了 break

  1. x=1
  2. while (1) {
  3. if ( x==4 ) {
  4. x++
  5. continue
  6. }
  7. print "iteration", x
  8. if ( x>20 ) {
  9. break
  10. }
  11. x++
  12. }

continue在for中使用

  1. #!/bin/awk
  2. BEGIN{
  3. for (x=1;x<=21;x++){
  4. if (x==4){
  5. continue
  6. }
  7. print "iteration",x
  8. }
  9. }

数组

AWK 中的数组都是关联数组,数字索引也会转变为字符串索引

  1. #!/bin/awk
  2. BEGIN{
  3. cities[1]="beijing"
  4. cities[2]="shanghai"
  5. cities["three"]="guangzhou"
  6. for( c in cities) {
  7. print cities[c]
  8. }
  9. print cities[1]
  10. print cities["1"]
  11. print cities["three"]
  12. }

用 awk 中查看服务器连接状态并汇总

  1. [root@node-1 ~]# netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}'
  2. LISTEN 8
  3. ESTABLISHED 1

常用字符串函数

Linux三剑客之awk - 图2
Linux三剑客之awk - 图3
字符串函数的应用
在 info 中查找满足正则表达式, /[0-9]+/ 用”!”替换,并且替换后的值,赋值给 info

  1. [root@node-1 ~]# awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}'
  2. this is a test!test!

如果查找到数字则匹配成功返回 ok,否则失败,返回未找到

  1. [root@node-1 ~]# awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}'
  2. ok

从第 4 个 字符开始,截取 10 个长度字符串

  1. [root@node-1 ~]# awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}'
  2. s is a tes

分割 info,动态创建数组 tA,awk for …in 循环,是一个无序的循环。 并不是从数组下标1…n 开始

  1. [root@node-1 ~]# awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}'
  2. 4
  3. 4 test
  4. 1 this
  5. 2 is
  6. 3 a