简单介绍一下,awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大,这是我们玩linux的必备基本功,若要对其身世有更详尽的了解,自行搜索即可。对于工具的知识,笔者尽量将每个知识点的简要说明,并给出实例。

简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

用法:
awk [options] 'scripts' file1,file2...
awk [options] 'pattern {action}' file1,file2...

options是awk的支持的选项,譬如-F -v等; scripts是其处理脚本,包含模式pattern和动作action(模式和动作的关系一般为,模式负责确定有效字段,动作负责对其处理)

一、print的简单使用

创建一个简单的测试文件如下:

   
   
   
   
  1. [root@mos download]# cat demo.txt  
  2. Welcome to mos blog. 
  3. This is a test file. 


例:打印整行: $0

   
   
   
   
  1. [root@mos download]# awk '{print $0}' demo.txt  
  2. Welcome to mos blog.  
  3. This is a test file. 

例:打印每行的最后一个字段: $NF

   
   
   
   
  1. [root@mos download]# awk '{print $NF}' demo.txt  
  2. blog.  
  3. file. 

例:打印第二个字段: $2

   
   
   
   
  1. [root@mos download]# awk '{print $2}' demo.txt  
  2. to  
  3. is 

例:打印每行的倒数第二个字段,并在其后打印OK

   
   
   
   
  1. [root@mos download]# awk '{print $(NF-1),"OK"}' demo.txt  
  2. mos OK  
  3. test OK 

例:打印行号

   
   
   
   
  1. [root@mos download]# awk '{print NR,$0}' demo.txt  
  2. 1 Welcome to mos blog.  
  3. 2 This is a test file. 

例:打印当前系统环境变量的某个特定值,譬如路径,下面俩输出一样

   
   
   
   
  1. [root@mos download]# awk '{print ENVIRON["USER"];}' demo.txt  
  2. root 
  3. root 
  4. [root@mos download]# awk 'BEGIN{print ENVIRON["PATH"];}' 
  5. /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin 
  6. PS:后面的文件只是为了让print成功执行,BEGIN是执行前可以输出的数据,常用作写为报告标题和测试输出,其内容不能获得与文件修改操作有关的变量,后面会说到;

例:awk的默认分隔符为空格,而有些文本字以其他字符为分隔符,以下两例相同

   
   
   
   
  1. [root@mos download]# awk -F: {'print $1,$NF'} /etc/passwd|tail -1  
  2. mos1 /bin/bash  
  3. [root@mos download]# awk -v FS=: '{print $1,$NF}' /etc/passwd|head -1  
  4. root /bin/bash 

例:修改输出分隔符号,特殊字符需转义,如下几例:

   
   
   
   
  1. [root@mos download]# awk -v OFS=. '{print $1,$NF}' demo.txt  
  2. Welcome.blog.  
  3. This.file.  
  4. [root@mos download]# awk -v OFS="~" '{print $1,$NF}' demo.txt  
  5. Welcome~blog.  
  6. This~file.  
  7. [root@mos download]# awk -v OFS=\" '{print $1,$NF}' demo.txt  
  8. Welcome"blog.  
  9. This"file.  
  10. [root@mos download]# awk -v OFS=9 '{print $1,$NF}' demo.txt  
  11. Welcome9blog.  
  12. This9file.  
  13. [root@mos download]# awk -v OFS=XXXX '{print $1,$NF}' demo.txt  
  14. WelcomeXXXXblog.  
  15. ThisXXXXfile.  
  16. PS: 若在print输出字段的之间不写逗号,则输出分隔符无效,输出的结果会直接连在一起 

awk中有许多默认的变量,例如之前列出的:$1~$n、$0、FS、OFS等。下图为awk内置变量表:

   
   
   
   
  1. 属性              说明  
  2. $0             当前记录(作为单个变量)  
  3. $1~$n          当前记录的第n个字段,字段间由FS分隔  
  4. FS             输入字段分隔符 默认是空格  
  5. NF             当前记录中的字段个数,就是有多少列  
  6. NR             已经读出的记录数,就是行号,从1开始  
  7. RS             输入的记录他隔符默 认为换行符  
  8. OFS            输出字段分隔符 默认也是空格  
  9. ORS            输出的记录分隔符,默认为换行符  
  10. ARGC           命令行参数个数  
  11. ARGV           命令行参数数组  
  12. FILENAME       当前输入文件的名字  
  13. IGNORECASE     如果为真,则进行忽略大小写的匹配  
  14. ARGIND         当前被处理文件的ARGV标志符  
  15. CONVFMT        数字转换格式 %.6g  
  16. ENVIRON        UNIX环境变量  
  17. ERRNO          UNIX系统错误消息  
  18. FIELDWIDTHS    输入字段宽度的空白分隔字符串  
  19. FNR            当前记录数  
  20. OFMT           数字的输出格式 %.6g  
  21. RSTART         被匹配函数匹配的字符串首  
  22. RLENGTH        被匹配函数匹配的字符串长度  
  23. SUBSEP        \034  
  24.     声明一下:由于许多变量笔者亦不怎么常用,因此笔者此处未全部列出。 

 

二、printf的使用

格式:
    printf format1,format2..., item1,item2...
PS:与print不同的是,首先,其多了个f;然后,其不会自动换行,而且还需要对每个字段指定输出格式。听起来很麻烦,用起来很爽的。

例:左右对齐打印输出,中间的符号笔者自己添加的,双引号和反斜杠前使用反斜杠转义即可;

   
   
   
   
  1. [root@mos download]# awk '{printf "%15s->%10s\n",$1,$NF}' demo.txt  
  2.         Welcome->     blog.  
  3.            This->     file.  
  4. [root@mos download]# awk '{printf "%-15s#%10s\n",$1,$NF}' demo.txt  
  5. Welcome        #     blog.  
  6. This           #     file.  
  7. [root@mos download]# awk '{printf "%-15s'\''%10s\n",$1,$NF}' demo.txt  
  8. Welcome        '     blog.  
  9. This           '     file.  
  10.     PS:一个单引号的写法太贱了,还有%,得写三个..各种无力吐槽 

例:以特定格式输出passwd字符

   
   
   
   
  1. [root@mos download]# awk -F: '{printf "%-15s->%10s    ->%20s\n",$1,$(NF-3),$NF}' /etc/passwd|head -2  
  2. root           ->         0    ->           /bin/bash  
  3. bin            ->         1    ->       /sbin/nologin 

例:以几种不同的计数方法打印一些数字

   
   
   
   
  1. [root@mos ~]# awk 'BEGIN{n1=111.29;n2=-3.444;n3=5.6789;printf("%.2f,%.2u,%.2g,%X,%o\n",n1,n2,n3,n1,n1);}' 
  2. 111.29,18446744073709551613,5.7,6F,157 

printf的格式都以%开头,后跟字符:具体如下:  

   
   
   
   
  1. 格式符        说明  
  2. %d        十进制有符号整数  
  3. %u        十进制无符号整数  
  4. %f        浮点数  
  5. %s        字符串  
  6. %c        显示字符的ASCII码  
  7. %p        指针的值  
  8. %e        科学技术法显示数值  
  9. %x        %X 无符号以十六进制表示的整数  
  10. %o        无符号以八进制表示的整数  
  11. %g        %G 以科学计数法或浮点数的格式显示数值  
  12. %%        显示其自身 
  13.  
  14. 修饰符:  
  15. -:  左对齐     这个好使,下面俩试了试,笔者没试出啥感觉..  
  16. +:  显示数值符号  
  17. N: 显示 

三、输出重定向
awk的输出结果可以直接使用输出重定向和管道,其作用和命令结束再次使用重定向和管道的效果类似,笔者在此不长篇叙述,仅作简单示范和介绍:
print items > output_file
print items >> output_file
print items | command

特殊文件描述符:
/dev/stdin:    标准输入
/dev/sdtout: 标准输出
/dev/stderr:  错误输出
/dev/fd/N:    某特定文件描述符,如/dev/stdin就相当于/dev/fd/0;

例:将结果输出到当前目录下的某个文本中;下两例效果一样

   
   
   
   
  1. # awk -F: '{printf "%-15s->%+10s->%20s\n",$1,$(NF-2),$NF > "test.txt" }' /etc/passwd  
  2. # awk -F: '{printf "%-15s->%+10s->%20s\n",$1,$(NF-2),$NF }' /etc/passwd > test1.txt 

 

awk的操作符,这些操作符和shell编程中的意思大都一致,因此不做详细解说,又因为单单一个的实例难以表达,笔者下方列出完之后,实例部分大都混合使用,笔者会一一说明。


一、算数操作符

   
   
   
   
  1. 操作符     描述 
  2. -x        负值  
  3. +x        转换为数值  
  4. x^y       次方  
  5. x**y      次方  
  6. x*y       乘法  
  7. x/y       除法  
  8. x+y       加法  
  9. x-y       减法  
  10. x%y       取余  

二、字符串操作符:
直接将print输出的结果之间不适用逗号或自定义OFS,即可,其实这是一个空值
例如:以下两例效果一样

   
   
   
   
  1. [root@mos download]# awk '{print $1 $2}' demo.txt  
  2. Welcometo  
  3. Thisis  
  4. [root@mos download]# awk '{print $1$2}' demo.txt  
  5. Welcometo  
  6. Thisis  

三、赋值操作符

   
   
   
   
  1. 操作符     描述 
  2. =         赋值操作符  
  3. +=        赋值加操作符  
  4. -=        赋值减操作符  
  5. *=        赋值乘操作符  
  6. /=        赋值除操作符  
  7. %=        赋值求余操作符  
  8. ^=        赋值求幂操作符  
  9. **=       赋值求幂操作符  
  10.     PS:需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代; 

四、布尔值
awk中,任何非0值或非空字符串都为真,反之就为假;
五、比较操作符

   
   
   
   
  1. 操作符     描述  
  2. >         大于  
  3. <         小于  
  4. >=        大于等于  
  5. <=        小于等于  
  6. ==        等于  
  7. !=        不等于  
  8. ~         匹配  
  9. !~        匹配取反  

六、表达式间的逻辑关系符 
&& :逻辑与
||     :逻辑或

例:
打印系统上uid大于等于500且以m开头的行的首字段;&&与

   
   
   
   
  1. [root@mos ~]# awk -F: '$3>=500 && $1 ~ /^m/{print $1}' /etc/passwd  
  2. mos  
  3. mos1  

打印系统上uid大于等于500或以v开头的行的首字段;||:或 

   
   
   
   
  1. [root@mos ~]# awk -F: '$3>=500 || $1 ~/^v/{print $1}' /etc/passwd  
  2. vcsa  
  3. nfsnobody  
  4. student  
  5. visitor  
  6. mos  
  7. mos1  

打印系统上uid大于500的以v和m开头的行的首字段:

   
   
   
   
  1. [root@mos ~]# awk -F: '$3>=500 && $1 ~/^m/|| $3>=500 && $1 ~/^v/{print $1}' /etc/passwd 
  2. visitor  
  3. mos  
  4. mos1  

七、条件表达式
selector?if=true=exp:if=false=exp
selector是条件,例如使用index函数判断一个变量中有无特定字符;有无则输出特定表达式if=true=exp:if=false=exp,如下例:

在变量值中做查找判断,index是awk的内置函数

   
   
   
   
  1. [root@mos ~]# awk 'BEGIN{msg="My name is MOS!";print index(msg,"MOS")?"ok":"no";}'  
  2. ok  
  3. [root@mos ~]# awk 'BEGIN{msg="My name is MOS!";print index(msg,"AAA")?"ok":"no";}'  
  4. no 

八、函数调用
function_name (parament1,parament2...)
用法
...您看上面,index函数的例子...对头,就那么用的。关于函数和以上的例子,在awk的模式介绍完后,基本都会有.

awk的模式
上面的关于逻辑运算符&&和||的例子中,已经用到了awk的模式,其以m和v开头的和大于等于500的运算,分别是表达式expression和正则表达式regexp,需要说明的是,其还支持取反和模糊匹配等,如下例及各种例:

例:打印/etc/passwd中以root开头的相应字段和非以root开头的相应字段

   
   
   
   
  1. [root@mos download]# awk -F: '/^root/{print $1,$NF}' /etc/passwd  
  2. root /bin/bash  
  3. [root@mos download]# awk -F: '!/^root/{print $1,$NF}' /etc/passwd|head -2  
  4. bin /sbin/nologin  
  5. daemon /sbin/nologin 

例:第三个字段有0的打印,和仅为0的,模糊匹配和精确匹配

   
   
   
   
  1. [root@mos ~]# awk -F: '$3~0{print}' /etc/passwd  
  2. root:x:0:0:root:/root:/bin/bash  
  3. uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin  
  4. .........  
  5. [root@mos ~]# awk -F: '$3==0{print}' /etc/passwd  
  6. root:x:0:0:root:/root:/bin/bash 

除了上面用到的表达式,还有ranges和BEGIN/END,其分别为范围匹配和特殊模式;例子如下:

例:打印从visitor开头的行到mos1开头的行中的所有行:ranges范围匹配

   
   
   
   
  1. [root@mos ~]# awk '/^visitor/,/^mos1/{print}' /etc/passwd  
  2. visitor:x:501:501::/home/visitor:/bin/bash  
  3. mos:x:502:502::/home/mos:/bin/bash  
  4. mos1:x:503:503::/home/mos1:/bin/bash 

例:打印结果添加标题和结束语:BEGIN和END的使用

   
   
   
   
  1. [root@mos download]# awk -F: 'BEGIN {print "Name\tShell"}{print $1,"\t",$NF}END{print "---End---"}' /etc/passwd 
  2. Name    Shell 
  3. root     /bin/bash 
  4. ................
  5. mysql    /bin/bash 
  6. ---End--- 

例:在条件执行之前,先打印标题字段,并在结束后,打印结束字段

   
   
   
   
  1. [root@mos ~]# awk -F: 'BEGIN{print "Username    UID"}$3>=500 && $1 ~ /^m/{printf "%-15s%s\n",$1,$3}END{print "---END---"}' /etc/passwd  
  2. Username    UID  
  3. mos            502  
  4. mos1          503  
  5. ---END---  
  6. [root@mos ~]# awk -F: 'BEGIN{print "Username    UID"}{printf "%-15s%s\n",$1,$3}END{print "---END---"}' /etc/passwd    去掉条件匹配,打印所有. 

例:统计有某个关键字的行的总行数,并输出BEGIN和END信息,其中count++表示:每遇一个相同的则加1,最后输出总数

   
   
   
   
  1. [root@mos download]# awk -F: 'BEGIN {count=0}$NF ~/nologin/ {count++}END{print "Nologin number is:",count}' /etc/passwd 
  2. Nologin number is: 30 

例:使用BEGIN也可以直接显示字符串

   
   
   
   
  1. [root@mos ~]# awk  'BEGIN{print "a""b"}'  
  2. ab  
  3. [root@mos ~]# awk  'BEGIN{print "a","b"}'  
  4. a b  
  5. [root@mos ~]# awk -v OFS=\~ 'BEGIN{print "a","b"}'  
  6. a~b 

例:BEGIN也可以给字段分隔符变量赋值

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{FS=":"}{print $1}' /etc/passwd|head -2 
  2. root 
  3. bin 

例:打印判断系统用户和普通用户输出:if条件判断和多条件判断

   
   
   
   
  1. [root@mos download]# awk -F: '{if ($3<=499)print $1,"Sys User";else print $1,"Common User."}' /etc/passwd  
  2. root Sys User 
  3. ........... 
  4. mos1 Common User
  5. mysql Sys User 
  6. [root@mos download]# awk -F: '{if($3==0){print $1,"Admin User";}else if($3>0 && $3<=499){print $1,"Sys User";}else{print $1,"Comm User"}}' /etc/passwd 
  7. root Admin User 
  8. bin Sys User 
  9. ............. 
  10. nfsnobody Comm User 
  11. ............ 
  12. oprofile Sys User 
  13. .................... 
  14. mos1 Comm User 
  15. mysql Sys User 

例:使用while打印/etc/passwd中非root且非空行的前四个字段:while语句

   
   
   
   
  1. [root@mos download]# awk -F: '$1!~/root/ && $1 !~/^$/{i=1;while(i<=4){print $i;i++}}' /etc/passwd|head -4 
  2. bin 

例:打印不包含root行的每行的奇数和偶数字段:while和for循环

   
   
   
   
  1. # awk -F: '$1!~/root/{i=1;while(i<=NF){print $i;i+=2}}' /etc/passwd    while奇  
  2. # awk -F: '$1!~/root/{i=2;while(i<=NF){print $i;i+=2}}' /etc/passwd  while偶  
  3. # awk -F: '{for(i=1;i<=NF;i+=2) print $i}' /etc/passwd        for奇  
  4. # awk -F: '{for(i=2;i<=NF;i+=2) print $i}' /etc/passwd        for偶 

例:数组的引用,下标可以是字符串,但需要双引号,数字则不需要

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{A[10]="hello";A[15]="world";print A[10],A[15]}' 
  2. hello world 
  3. [root@mos download]# awk 'BEGIN{A["m"]="hello";A["n"]="world";print A["m"],A["n"]}' 
  4. hello world 

例:数组的遍历

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{A["m"]="hello";A["n"]="world";for(B in A)print A[B],"OK"}' 
  2. hello OK 
  3. world OK 

例:遍历环境变量关联数组,并以其下标加等号打印出:数组和环境变量

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{for(k in ENVIRON){print k"="ENVIRON[k],"OK";}}' 
  2. AWKPATH=.:/usr/share/awk OK 
  3. OLDPWD=/root OK 
  4. .........

例:统计以tcp开头网络状态的连接数:模式匹配、for循环和数组

   
   
   
   
  1. [root@mos download]# netstat -ant|awk '$1~/tcp/{S[$NF]++}END{for (A in S) printf "%-15s:%s\n",A,S[A]}' 
  2. LISTEN         :11 
  3. ESTABLISHED    :2 

例:统计不为空的用户的shell数量

   
   
   
   
  1. [root@mos download]# awk -F: '$NF!~/^$/{S[$NF]++}END{for (A in S)print A,S[A]}' /etc/passwd 
  2. /bin/sync 1 
  3. /bin/bash 6 
  4. /sbin/nologin 30 
  5. /sbin/halt 1 
  6. /sbin/shutdown 1 

例:打印100以内的加法,使用while和do~while

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{res=0;i=0;do{res+=i;i++;}while(i<=100)print res;}' 
  2. 5050 
  3. [root@mos download]# awk 'BEGIN{while(i<=100){res+=i;i++;}print res;}'  
  4. 5050 

例:awk执行计算的速度是shell的近40倍   

   
   
   
   
  1. [root@mos ~]# time(awk 'BEGIN{res=0;for(i=0;i<=10000;i++){res+=i;}print res;}')  
  2. 50005000  
  3. real    0m0.003s  
  4. user    0m0.001s  
  5. sys    0m0.001s  
  6. [root@mos ~]# time(res=0;for i in $(seq 10000);do res=$(($res+i));done;echo $res;)  
  7. 50005000  
  8. real    0m0.114s  
  9. user    0m0.097s  
  10. sys    0m0.011s 

例:统计apache常规日志的IP的访问次数,最多的前5个

   
   
   
   
  1. # awk '{IP[$1]++}END{for(A in IP)print IP[A],A}' access_log|sort -rn|head -5 

 

awk一些内部函数的使用

例:取1000以内的随机数

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{srand();fr=int(1000*rand());print fr;}' 
  2. 796 
  3. [root@mos download]# awk 'BEGIN{srand();fr=int(1000*rand());print fr;}' 
  4. 224 

例:gsub的使用,将lab变量的任意数字替换为--demo--后并输出

   
   
   
   
  1. [root@mos ~]# awk 'BEGIN{lab="2My name is MOS2012.";gsub(/[0-9]+/,"--demo--",lab);print lab}'  
  2. --demo--My name is MOS--demo--. 

例:正则表达式匹配,match函数的使用

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{res="My name is MOS!";print match(res,/[0-9]+/)?"ok":"no";}' 
  2. no 
  3. [root@mos download]# awk 'BEGIN{res="My name is MOS88!";print match(res,/[0-9]+/)?"ok":"no";}' 
  4. ok 

例:截取字符串,substr的使用,从第四个字符,截取四个字符长度

   
   
   
   
  1. [root@mos ~]# awk 'BEGIN{info="My Name is MOS!";print substr(info,4,4);}'  
  2. Name 

例:字符串分割,split和length的使用,先打印总长度,然后打印每个数组中每个值和其下标

   
   
   
   
  1. [root@mos ~]# awk 'BEGIN{test="My name is MOS.";split(test,Ary," ");for(l in Ary){print l,Ary[l];}print length(Ary),"Str Len";}'  
  2. 4 MOS.  
  3. 1 My  
  4. name  
  5. is  
  6. 4 Str Len 

例:打开外部文件,close函数的用法,getline打开管道,将读取内容送给print

   
   
   
   
  1. [root@mos ~]# awk -v FS=: 'BEGIN{while("cat /etc/passwd"|getline){print $1;};close("/etc/passwd");}'|head -3  
  2. root  
  3. bin  
  4. daemon 

例:逐行读取外部文件,getline用法,其实上一个也列出了getline的使用方法.

   
   
   
   
  1. [root@mos ~]# awk 'BEGIN{while(getline<"/etc/passwd"){print $0,"OK";};close("/etc/passwd");}'|tail -3  
  2. mos:x:502:502::/home/mos:/bin/bash OK  
  3. mos1:x:503:503::/home/mos1:/bin/bash OK  
  4. mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash OK 

例:调用外部应用程序

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{D=system("df -hP");print D;}' 
  2. Filesystem            Size  Used Avail Use% Mounted on 
  3. /dev/mapper/My_vg-lv_root  9.7G  3.1G  6.2G  33% / 
  4. /dev/mapper/My_vg-lv_home  496M   19M  452M   4% /home 
  5. /dev/sda1              99M   13M   82M  13% /boot 
  6. tmpfs                1014M     0 1014M   0% /dev/shm 
  7. /dev/hdc              3.3G  3.3G     0 100% /rhel_dvd 

例:获取shell变量

   
   
   
   
  1. [root@mos ~]# Out="My name is MOS."  
  2. [root@mos ~]# echo|awk '{print res}' res="$Out"  
  3. My name is MOS.  
  4. [root@mos ~]# echo|awk -v res1="$Out" 'BEGIN{print res1}'  
  5. My name is MOS.  
  6. [root@mos ~]# echo|awk -v res1="$Out" '{print res1}'  
  7. My name is MOS. 

例:时间函数mktime、strftime和system的使用:输出一个指定时间,两个时间差,指定时间跟当前时间差

   
   
   
   
  1. [root@mos download]# awk 'BEGIN{Tim=mktime("2012 12 21 23 59 59");print strftime("%c",Tim);}'  
  2. Fri 21 Dec 2012 11:59:59 PM CST 
  3. [root@mos download]# awk 'BEGIN{Time=mktime("2012 12 21 23 59 59");Time1=mktime("2012 12 21 23 59 01");print Time-Time1;}' 
  4. 58 
  5. [root@mos download]# awk 'BEGIN{Time=mktime("2012 12 21 23 59 59");Time1=systime();print Time1-Time;}' 
  6. 535294 

 

到此为止,以上笔者缩写,若都可以理解,并自己改写成所需要的,基本上awk的常规使用,应是没问题了,当然,笔者亦未全部说完相关知识点,每个知识,若要学透,皆非朝夕之事,一起努力吧!