linux命令终极系列awk

来源:互联网 发布:mac上的翻墙软件 编辑:程序博客网 时间:2024/06/01 08:56


AWK man 手册:http://man.linuxde.net/awk

AWK编程的内容极多,这里只罗列简单常用的用法,更多请参考 http://www.gnu.org/software/gawk/manual/gawk.html


简介

什么是awk? 

        awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说,awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。即awk处理过程: 依次对每一行进行处理,然后输出。

        awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。

        awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。正是这三个人创造了awk---一个优秀的样式扫描与处理工具。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言, 三位创建者已将它正式定义为“样式扫描和处理语言”。 它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。


AWK的功能是什么?

        与sed和grep很相似,awk是一种样式扫描与处理工具。但其功能却大大强于sed和grep。awk提供了极其强大的功能:它几乎可以完成grep和sed所能完成的全部工作,同时,它还可以可以进行样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。它具备了一个完整的语言所应具有的几乎所有精美特性。实际上,awk的确拥有自己的语言:awk程序设计语言,awk的三位创建者已将它正式定义为:样式扫描和处理语言。

为什么使用awk?

        使用awk的第一个理由是基于文本的样式扫描和处理是我们经常做的工作,awk所做的工作有些象数据库,但与数据库不同的是,它处理的是文本文件,这些文件没有专门的存储格式,普通的人们就能编辑、阅读、理解和处理它们。而数据库文件往往具有特殊的存储格式,这使得它们必须用数据库处理程序来处理它们。既然这种类似于数据库的处理工作我们经常会遇到,我们就应当找到处理它们的简便易行的方法,UNIX有很多这方面的工具,例如sed 、grep、sort以及find等等,awk是其中十分优秀的一种。 

        使用awk的第二个理由是awk是一个简单的工具,当然这是相对于其强大的功能来说的。的确,UNIX有许多优秀的工具,例如UNIX天然的开发工具C语言及其延续C++就非常的优秀。但相对于它们来说,awk完成同样的功能要方便和简捷得多。这首先是因为awk提供了适应多种需要的解决方案:从解决简单问题的awk命令行到复杂而精巧的awk程序设计语言,这样做的好处是,你可以不必用复杂的方法去解决本来很简单的问题。例如,你可以用一个命令行解决简单的问题,而C不行,即使一个再简单的程序,C语言也必须经过编写、编译的全过程。其次,awk本身是解释执行的,这就使得awk程序不必经过编译的过程,同时,这也使得它与shell script程序能够很好的契合。最后,awk本身较C语言简单,虽然awk吸收了C语言很多优秀的成分,熟悉C语言会对学习awk有很大的帮助,但awk本身不须要会使用C语言――一种功能强大但需要大量时间学习才能掌握其技巧的开发工具。
基于以上理由,再加上awk强大的功能,我们有理由说,如果你要处理与文本样式扫描相关的工作,awk应该是你的第一选择。在这里有一个可遵循的一般原则:如果你用普通的shell工具或shell script有困难的话,试试awk,如果awk仍不能解决问题,则便用C语言,如果C语言仍然失败,则移至C++。 

AWK调用方式

有三种方式调用awk

1.AWK命令行方式      awk [-F  field-separator]  'commands'  input-file(s)      其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的文件。      在awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。    2.shell脚本方式      将一段awk程序写入文本文件,然后在它的第一行加上:     #!/bin/awk -f     并赋予这个文本文件以执行的权限。    $awk脚本文本名 待处理文件   3.使用-f选项调用awk程序。awk允许将一段awk程序写入一个文本文件,然后在awk命令行中用-f选项调用并执行这段程序。      awk -f awk-script-file input-file(s)      其中,-f选项加载awk-script-file中的awk脚本,input-file(s)跟上面的是一样的。 

AWK语法

 基本语法awk [opion] 'awk_script' input_file1 [input_file2 ...]awk的常用选项option有:    -F fs : 使用fs作为输入记录的字段分隔符,如果省略该选项,awk使用环境变量IFS的值    -f filename : 从文件filename中读取awk_script    -v var=value : 为awk_script设置变量awk '{pattern + action}' {filenames} awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’ file   [-F|-f|-v]   大参数,-F指定分隔符,-f调用脚本,-v定义变量 var=value  '  '          引用代码块  BEGIN   初始化代码块,在对每一行进行处理之前,初始化代码,主要是引用全局变量,设置FS分隔符  //      匹配代码块,可以是字符串或正则表达式  {}      命令代码块,包含一条或多条命令  ;       多条命令使用分号分隔  END     结尾代码块,在对每一行进行处理之后再执行的代码块,主要是进行最终计算或输出结尾摘要信息  file    awk的输入文件,可以有多个。注意:awk不会修改输入文件。如果没有指定输入文件,那么默认为标准输入(屏幕)

awk的变量

awk提供两种变量:
(1) 内置变量
  在引用时,不需要加$
(2) 自定义变量
        awk中引用自定义变量必须在它前面加上标志符"$"。awk根据其在awk中第一次出现的形式和上下文确定其具体的数据类型。当变量类型不确定时,awk默认其为字符串类型。这里有一个技巧:如果你要让你的awk程序知道你所使用的变量的明确类型,你应当在在程序中给它赋初值。

awk的记录,字段与内置变量
 1. 记录与字段
在 awk中,缺省的情况下总是将文本文件中的一行视为一个记录,而将一行中的某一部分作为记录中的一个字段。为了操作这些不同的字段,awk借用shell 的方法,用$1,$2,$3...这样的方式来顺序地表示行(记录)中的不同字段。特殊地,awk用$0表示整个行(记录)。不同的字段之间是用称作分隔 符的字符分隔开的。系统默认的分隔符是空格。awk允许在命令行中用-F re的形式来改变这个分隔符。事实上,awk用一个内置的变量FS来记忆这个分隔符
  2. 内置变量
  
awk中有好几个这样的内置变量,例如,记录分隔符变量RS、当前工作的记录数NR等等。
  示例:显示文件本文件testAwk中第3行到第6行,以字符%分隔的第一个字段,第三个字段:
 awk -F % 'NR==3,NR==6 {printf $1  $3}' testAwk
   
  示例:杀死系统中所有top进程
    ps -ef|grep " top" |grep -v "grep"|awk '{printf $2}'|xargs kill -9
    或
    kill -9 `ps -ef|grep " top" |grep -v "grep"|awk '{printf $2}'`


$0    表示整个当前行  $1    每行第一个字段  NF    字段数量变量  NR    每行的记录号,多文件记录递增  FNR   与NR类似,不过多文件记录不递增,每个文件都从1开始  \t    制表符  \n    换行符  FS    BEGIN时定义分隔符  RS    输入的记录分隔符, 默认为换行符(即文本是按一行一行输入)  ~     匹配,与==相比不是精确比较  !~    不匹配,不精确比较  ==    等于,必须全部相等,精确比较  !=    不等于,精确比较  &&   逻辑与  ||    逻辑或  +     匹配时表示1个或1个以上  /[0-9][0-9]+/   两个或两个以上数字  /[0-9][0-9]*/   一个或一个以上数字  FILENAME        文件名  OFS             输出字段分隔符, 默认也是空格,可以改为制表符等  ORS             输出的记录分隔符,默认为换行符,即处理结果也是一行一行输出到屏幕  -F'[:#/]'       定义三个分隔符  

awk内置变量

awk有许多内置变量用来设置环境信息,这些变量可以被改变,下面给出了最常用的一些变量。

ARGC               命令行参数个数  ARGV               命令行参数排列  ENVIRON            支持队列中系统环境变量的使用  FILENAME           awk浏览的文件名  FNR                浏览文件的记录数  FS                 设置输入域分隔符,等价于命令行 -F选项  NF                 浏览记录的域的个数  NR                 已读的记录数  OFS                输出域分隔符  ORS                输出记录分隔符  RS                 控制记录分隔符  

此外,$0变量是指整条记录。$1表示当前行的第一个域,$2表示当前行的第二个域,......以此类推。
统计/etc/passwd:文件名,每行的行号,每行的列数,对应的完整行内容:

#awk  -F ':'  '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd  filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash  filename:/etc/passwd,linenumber:2,columns:7,linecontent:daemon:x:1:1:daemon:/usr/sbin:/bin/sh  filename:/etc/passwd,linenumber:3,columns:7,linecontent:bin:x:2:2:bin:/bin:/bin/sh  filename:/etc/passwd,linenumber:4,columns:7,linecontent:sys:x:3:3:sys:/dev:/bin/sh  

使用printf替代print,可以让代码更加简洁,易读

awk  -F ':'  '{printf("filename:%10s,linenumber:%s,columns:%s,linecontent:%s\n",FILENAME,NR,NF,$0)}' /etc/passwd  

print 和 printf

awk中同时提供了print和printf两种打印输出的函数。

其中print函数的参数可以是变量、数值或者字符串。字符串必须用双引号引用,参数用逗号分隔。

如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与输出文件的分隔符的作用是一样的,只是后者是空格而已。

printf函数,其用法和C语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。

格式化字符串输出(sprintf使用):

其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出; 另一部分是格式化规定字符, 以"%"开始, 后跟一个或几个规定字符,用来确定输出内容格式。

格式符说明%d十进制有符号整数%u十进制无符号整数%f浮点数%s字符串%c单个字符%p指针的值%e指数形式的浮点数%x%X 无符号以十六进制表示的整数%o无符号以八进制表示的整数%g自动选择合适的表示法

[chengmo@centos5 ~]$ awk 'BEGIN{n1=124.113;n2=-1.224;n3=1.2345; printf("%.2f,%.2u,%.2g,%X,%o\n",n1,n2,n3,n1,n1);}'124.11,18446744073709551615,1.2,7C,174

netstat -anp|awk '{printf "%-8s %-8s %-10s\n",$1,$2,$3}'   printf表示格式输出  %格式化输出分隔符  -8长度为8个字符  s表示字符串类型  打印每行前三个字段,指定第一个字段输出字符串类型(长度为8),第二个字段输出字符串类型(长度为8),  第三个字段输出字符串类型(长度为10)  netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-10s %-10s %-10s \n",$1,$2,$3}'  netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-3s %-10s %-10s %-10s \n",NR,$1,$2,$3}'  

字符转换: echo "65" |awk '{printf "%c\n",$0}' 输出Aawk 'BEGIN {printf "%f\n",999}' 输出999.000000格式化输出:awk '{printf "%-15s %s\n",$1,$3}' temp 将第一个域全部左对齐显示

print & $0

print    是awk打印指定内容的主要命令  awk '{print}'  /etc/passwd   ==   awk '{print $0}'  /etc/passwd    awk '{print " "}' /etc/passwd        //不输出passwd的内容,而是输出相同个数的空行,进一步解释了awk是一行一行处理文本  awk '{print "a"}'   /etc/passwd      //输出相同个数的a行,一行只有一个a字母  awk -F":" '{print $1}'  /etc/passwd   awk -F: '{print $1; print $2}'   /etc/passwd     //将每一行的前二个字段,分行输出,进一步理解一行一行处理文本  awk  -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd //输出字段1,3,6,以制表符作为分隔符  

-f 指定脚本文件

awk -f script.awk  file  BEGIN{  FS=":"  }  {print $1}       //效果与awk -F":" '{print $1}'相同,只是分隔符使用FS在代码自身中指定    awk 'BEGIN{X=0} /^$/{ X+=1 } END{print "I find",X,"blank lines."}' test   I find 4 blank lines.   ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is",sum}'                    //计算文件大小  total size is 17487  

-F 指定分隔符

$1  指指定分隔符后,第一个字段,$3第三个字段, \t是制表符  一个或多个连续的空格或制表符看做一个定界符,即多个空格看做一个空格  awk -F":" '{print $1}'  /etc/passwd  awk -F":" '{print $1 $3}'  /etc/passwd      //$1与$3相连输出,不分隔  awk -F":" '{print $1,$3}'  /etc/passwd      //多了一个逗号,$1与$3使用空格分隔  awk -F":" '{print $1 " " $3}'  /etc/passwd  //$1与$3之间手动添加空格分隔  awk -F":" '{print "Username:" $1 "\t\t Uid:" $3 }' /etc/passwd  //自定义输出    awk -F: '{print NF}' /etc/passwd        //显示每行有多少字段  awk -F: '{print $NF}' /etc/passwd       //将每行第NF个字段的值打印出来   awk -F: 'NF==4 {print }' /etc/passwd   //显示只有4个字段的行  awk -F: 'NF>2{print $0}' /etc/passwd    //显示每行字段数量大于2的行  awk '{print NR,$0}' /etc/passwd         //输出每行的行号  awk -F: '{print NR,NF,$NF,"\t",$0}' /etc/passwd   //依次打印行号,字段数,最后字段值,制表符,每行内容  awk -F: 'NR==5{print}'  /etc/passwd            //显示第5行  awk -F: 'NR==5 || NR==6{print}'  /etc/passwd   //显示第5行和第6行  route -n|awk 'NR!=1{print}'    //不显示第一行  

// 匹配代码块

//纯字符匹配   !//纯字符不匹配   ~//字段值匹配    !~//字段值不匹配   ~/a1|a2/字段值匹配a1或a2     awk '/mysql/' /etc/passwd  awk '/mysql/{print }' /etc/passwd  awk '/mysql/{print $0}' /etc/passwd    //三条指令结果一样  awk '!/mysql/{print $0}' /etc/passwd   //输出不匹配mysql的行  awk '/mysql|mail/{print}' /etc/passwd  awk '!/mysql|mail/{print}' /etc/passwd  awk -F: '/mail/,/mysql/{print}' /etc/passwd  //区间匹配  awk '/[2][7][7]*/{print $0}' /etc/passwd     //匹配包含27为数字开头的行,如27,277,2777...  awk -F: '$1~/mail/{print $1}' /etc/passwd    //$1匹配指定内容才显示  awk -F: '{if($1~/mail/) print $1}' /etc/passwd   //与上面相同  awk -F: '$1!~/mail/{print $1}' /etc/passwd       //不匹配  awk -F: '$1!~/mail|mysql/{print $1}' /etc/passwd          

IF 语句

if(表达式) #if ( Variable in Array )语句1else语句2格式中"语句1"可以是多个语句,如果你为了方便Unix awk判断也方便你自已阅读,你最好将多个语句用{}括起来。Unix awk分枝结构允许嵌套,其格式为:if (expression) {      statement;      statement;      ... ...  }    if (expression) {      statement;  } else {      statement2;  }    if (expression) {      statement1;  } else if (expression1) {      statement2;  } else {      statement3;  }  [chengmo@localhost nginx]# awk 'BEGIN{ test=100;if(test>90){    print "very good";}else if(test>60){    print "good";}else{    print "no pass";}}'very good每条命令语句后面可以用“;”号结尾。

if 必须用在{}中,且比较内容用()扩起来  awk -F: '{if($1~/mail/) print $1}' /etc/passwd       //简写  awk -F: '{if($1~/mail/) {print $1}}'  /etc/passwd    //全写  awk -F: '{if($1~/mail/) {print $1} else {print $2}}' /etc/passwd  //if...else...  

awk -F: '{if($3>100) print "large"; else print "small"}' /etc/passwd  small  small  small  large  small  small  awk -F: 'BEGIN{A=0;B=0} {if($3>100) {A++; print "large"} else {B++; print "small"}} END{print A,"\t",B}' /etc/passwd     //ID大于100,A加1,否则B加1  awk -F: '{if($3<100) next; else print}' /etc/passwd    //小于100跳过,否则显示  awk -F: 'BEGIN{i=1} {if(i<nf) print="" nr,nf,i++="" }'="" etc="" passwd=""   <="" span="" style="word-wrap: break-word;">  awk -F: 'BEGIN{i=1} {if(i<nf) {print="" nr,nf}="" i++="" }'="" etc="" passwd<="" span="" style="word-wrap: break-word;">  另一种形式  awk -F: '{print ($3>100 ? "yes":"no")}'  /etc/passwd   awk -F: '{print ($3>100 ? $3":\tyes":$3":\tno")}'  /etc/passwd    while语句  awk -F: 'BEGIN{i=1} {while(i<nf) print="" nf,$i,i++}'="" etc="" passwd ="" <="" div="" style="word-wrap: break-word;">  7 root 1  7 x 2  7 0 3  7 0 4  7 root 5  7 /root 6  

统计某个文件夹下的文件占用的字节数,过滤4096大小的文件(一般都是文件夹):

ls -l |awk 'BEGIN {size=0;print "[start]size is ", size} {if($5!=4096){size=size+$5;}} END{print "[end]size is ", size/1024/1024,"M"}'   [end]size is  8.22339 M  

循环语句(while,for,do)

awk的循环基本结构

For (element in array) print array[element]awk 'BEGIN {record="123#456#789";split(record,myarray,"#")} END { for (i in myarray) {print myarray[i]} }

1.while语句格式:while(表达式){语句}例子:[chengmo@localhost nginx]# awk 'BEGIN{ test=100;total=0;while(i<=test){    total+=i;    i++;}print total;}'50502.for 循环for循环有两种格式:格式1:for(变量 in 数组){语句}例子:[chengmo@localhost nginx]# awk 'BEGIN{ for(k in ENVIRON){    print k"="ENVIRON[k];}}'AWKPATH=.:/usr/share/awkOLDPWD=/home/web97SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpassSELINUX_LEVEL_REQUESTED=SELINUX_ROLE_REQUESTED=LANG=zh_CN.GB2312。。。。。。说明:ENVIRON 是awk常量,是子典型数组。格式2:for(变量;条件;表达式){语句}例子:[chengmo@localhost nginx]# awk 'BEGIN{ total=0;for(i=0;i<=100;i++){    total+=i;}print total;}'50503.do循环格式:do{语句}while(条件)例子:[chengmo@localhost nginx]# awk 'BEGIN{ total=0;i=0;do{    total+=i;    i++;}while(i<=100)print total;}'5050
以上为awk流程控制语句,从语法上面大家可以看到,与c语言是一样的。
有了这些语句,其实很多shell程序都可以交给awk,而且性能是非常快的。
break 当 break 语句用于 while 或 for 语句时,导致退出程序循环。
continue 当 continue 语句用于 while 或 for 语句时,使程序循环移动到下一个迭代。
next 能能够导致读入下一个输入行,并返回到脚本的顶部。这可以避免对当前输入行执行其他的操作过程。
exit 语句使主输入循环退出并将控制转移到END,如果END存在的话。如果没有定义END规则,或在END中应用exit语句,则终止脚本的执行。

条件表达式

==   !=   >   >=    awk -F":" '$1=="mysql"{print $3}' /etc/passwd    awk -F":" '{if($1=="mysql") print $3}' /etc/passwd    //与上面相同   awk -F":" '$1!="mysql"{print $3}' /etc/passwd         //不等于  awk -F":" '$3>1000{print $3}' /etc/passwd     //大于  awk -F":" '$3>=100{print $3}' /etc/passwd     //大于等于  awk -F":" '$3<1{print $3}' /etc/passwd        //小于  awk -F":" '$3<=1{print $3}' /etc/passwd       //小于等于  

逻辑运算符

&& ||   awk -F: '$1~/mail/ && $3>8 {print }' /etc/passwd        //逻辑与,$1匹配mail,并且$3>8  awk -F: '{if($1~/mail/ && $3>8) print }' /etc/passwd  awk -F: '$1~/mail/ || $3>1000 {print }' /etc/passwd     //逻辑或  awk -F: '{if($1~/mail/ || $3>1000) print }' /etc/passwd   

数值运算

awk -F: '$3 > 100' /etc/passwd      awk -F: '$3 > 100 || $3 < 5' /etc/passwd    awk -F: '$3+$4 > 200' /etc/passwd  awk -F: '/mysql|mail/{print $3+10}' /etc/passwd   //第三个字段加10打印   awk -F: '/mysql/{print $3-$4}' /etc/passwd        //减法  awk -F: '/mysql/{print $3*$4}' /etc/passwd        //求乘积  awk '/MemFree/{print $2/1024}' /proc/meminfo      //除法  awk '/MemFree/{print int($2/1024)}' /proc/meminfo  //取整  

输出分隔符OFS

awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt  awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt          //输出字段6匹配WAIT的行,其中输出每行行号,字段4,5,6,并使用制表符分割字段  

输出处理结果到文件

1. 在命令代码块中直接输出  route -n|awk 'NR!=1{print > "./fs"}'     2. 使用重定向进行输出     route -n|awk 'NR!=1{print}'  > ./fs  

数组

因为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格里。由于hash不是顺序存储,因此在显示数组内容时会发现,它们并不是按照你预料的顺序显示出来的。数组和变量一样,都是在使用时自动创建的,awk也同样会自动判断其存储的是数字还是字符串。一般而言,awk中的数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。

netstat -anp|awk 'NR!=1{a[$6]++} END{for (i in a) print i,"\t",a[i]}'  netstat -anp|awk 'NR!=1{a[$6]++} END{for (i in a) printf "%-20s %-10s %-5s \n", i,"\t",a[i]}'  9523                               1       9929                               1       LISTEN                            6       7903                               1       3038/cupsd                   1       7913                               1       10837                             1       9833                               1    

显示/etc/passwd的账户

awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd  0 root  1 daemon  2 bin  3 sys  4 sync  5 games  ......  

其他awk用法

向一行awk命令传值:awk '{if ($5who | awk '{if ($1==user) print $1 " are in " $2 ' user=$LOGNAME 使用环境变量awk脚本命令:开头使用 !/bin/awk -f  ,如果没有这句话自含脚本将不能执行,例子:!/bin/awk -f# all comment lines must start with a hash '#'# name: student_tot.awk# to call: student_tot.awk grade.txt# prints total and average of club student points# print a header firstBEGIN{print "Student    Date   Member No.  Grade  Age  Points  Max"print "Name  Joined Gained  Point Available"print"========================================================="}# let's add the scores of points gained(tot+=$6);# finished processing now let's print the total and average pointEND{    print "Club student total points :" tot    print "Average Club Student points :" tot/N}

awk脚本

awk脚本可以由一条或多条awk_cmd组成,对于多个awk_cmd,一个awk_cmd完成后,应该另起一行,以便进行隔。 
awk_cmd由两部分组成: awk_pattern { actions }
另外,在awk命令中直接使用awk_script时,awk_script也可以被分成多行书写,但必须确保整个awk_script单引号括起来。
awk命令的一般形式:
awk ' BEGIN { actions }
awk_pattern1 { actions }
............
awk_patternN { actions }
END { actions }
' inputfile
其中 BEGIN { actions } 和 END { actions } 是可选的。
在awk脚本中可以使用AWK本身内置变量,如下: 
ARGC 命令行变元个数
ARGV 命令行变元数组
FILENAME 当前输入文件名
FNR 当前文件中的记录号
FS 输入域分隔符,默认为一个空格
RS 输入记录分隔符
NF 当前记录里域个数
NR 到目前为止记录数
OFS 输出域分隔符
ORS 输出记录分隔符

AWK脚本的运行过程:

awk的流程控制
BEGIN和END:
任何在BEGIN之后列出的操作(在{}内)将在awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。
例:累计销售文件xs中的销售金额(假设销售金额在记录的第三字段):

$awk
>'BEGIN { FS=":";print "统计销售金额";total=0}
>{print $3;total=total+$3;}
>END {printf "销售金额总计:%.2f",total}' sx
(注:>是shell提供的第二提示符,如要在shell程序awk语句和awk语言中换行,则需在行尾加反斜杠\)
在这里,BEGIN预置了内部变量FS(字段分隔符)和自定义变量total,同时在扫描之前显示出输出行头。而END则在扫描完成后打印出总合计。

 如果BEGIN 区块存在,awk执行它指定的actions。
 awk从输入文件中读取一行,称为一条输入记录。(如果输入文件省略,将从标准输入读取)
③ awk将读入的记录分割成字段,将第1个字段放入变量$1中,第2个字段放入$2,以此类推。$0表示整条记录。字段分隔符使用shell环境变量IFS或由参数指定。
 把当前输入记录依次与每一个awk_cmd中awk_pattern比较,看是否匹配,如果相匹配,就执行对应的actions。如果不匹配,就跳过对应的actions,直到比较完所有的awk_cmd。
 当一条输入记录比较了所有的awk_cmd后,awk读取输入的下一行,继续重复步骤③和④,这个过程一直持续,直到awk读取到文件尾。
 当awk读完所有的输入行后,如果存在END,就执行相应的actions。


1)input_file可以是一个文件或者多个文件的列表,awk将按顺序处理列表中的每个文件。
2)一条awk_cmdawk_pattern可以省略,省略时不对输入记录进行匹配比较就执行相应的actions。一条awk_cmd的actions 也可以省略,省略时默认的动作为打印当前输入记录,即{print $0} 。一条awk_cmd中的awk_pattern和actions不能同时省略
3) BEGIN区块END区块别位于awk_script的开头和结尾awk_script中只有END区块或者只有BEGIN区块是被允许的。如果awk_script中只有BEGIN { actions } ,awk不会读取input_file。
4) awk把输入文件的数据读入内存,然后操作内存中的输入数据副本,awk不会修改输入文件的内容。
5) awk的总是输出到标准输出,如果想让awk输出到文件,可以使用重定向。

awk_pattern

awk_pattern模式部分决定actions动作部分何时触发及触发actions

awk_pattern可以是以下几种类型:

1) 正则表达式用作awk_pattern: /regexp/
    注意,正则表达式regexp必须被/包起来
    awk中正则表达式匹配操作中经常用到的字符:
    \ ^ $ . [] | () * // :通用的regexp元字符
    + : 匹配其前的单个字符一次以上,是awk自有的元字符,不适用于grep或sed等
    ? : 匹配其前的单个字符1次或0次,是awk自有的元字符,不适用于grep或sed等
    关于正则表达式的更多内容请参《正则表达式》
    举例:
    awk '/ *\$0\.[0-9][0-9].*/' input_file
    比如,行内容为$0.99. helllo的行就可以和上面的正则表达式相配
2) 布尔表达式用作awk_pattern,表达式成立时,触发相应的actions执行。
    ① 表达式中可以使用变量(如字段变量$1,$2等)和/regexp/
    ② 布尔表达式中的操作符:
       关系操作符: < > <= >= == !=
       匹配操作符: value ~ /regexp/ 如果value匹配/regexp/,则返回真
       value !~ /regexp/ 如果value不匹配/regexp/,则返回真
        举例: awk '$2 > 10 {print "ok"}' input_file
        awk '$3 ~ /^d/ {print "ok"}' input_file
    ③ &&(与) 和 ||(或) 可以连接两个/regexp/或者布尔表达式,构成混合表达式!(非) 可以用于布尔表达式或者/regexp/之前。
       举例: awk '($1 < 10 ) && ($2 > 10) {print $0 "ok"}' input_file
       awk '/^d/ || /x${print $0 "ok"}' input_file
    ④ 其它表达式用作awk_script,如赋值表达式等
       举例:
       awk '(tot+=$6); END{print "total points :" tot }' input_file // 分号不能省略
       awk 'tot+=$6 {print $0} END{print "total points :" tot }' input_file // 与上面等效
   当使用赋值表达式时,表示如果赋值后的变量是数字的话,如果为非0,就匹配,否则不匹配;如果为字符串的话,非空就为匹配,否则不匹配。

awk内置字符串函数:
gsub(r,s)           在整个$0中用s替代r
awk 'gsub(/name/,"xingming") {print $0}' temp
gsub(r,s,t)         在整个t中用s替代r
index(s,t)          返回s中字符串t的第一位置
awk 'BEGIN {print index("Sunny","ny")}' temp     返回4
length(s)           返回s的长度
match(s,r)          测试s是否包含匹配r的字符串
awk '$1=="J.Lulu" {print match($1,"u")}' temp    返回4
split(s,a,fs)       在fs上将s分成序列a
awk 'BEGIN {print split("12#345#6789",myarray,"#")"'
返回3,同时myarray[1]="12", myarray[2]="345", myarray[3]="6789"
sprint(fmt,exp)     返回经fmt格式化后的exp
sub(r,s)   从$0中最左边最长的子串中用s代替r(只更换第一遇到的匹配字符串)
substr(s,p)         返回字符串s中从p开始的后缀部分
substr(s,p,n)       返回字符串s中从p开始长度为n的后缀部分

awk字符串连接操作 [chengmo@centos5 ~]$ awk 'BEGIN{a="a";b="b";c=(a""b);print c}'      

AWK 内置函数

awk内置函数,主要分以下3种类似:算数函数、字符串函数、其它一般函数、时间函数

1.以下示例了printf函数,它与C语言相似,如下,显示testAwk文件中行号与第1个字段:  $awk '{printf"%03d%s\n",NR,$1}' testAwk2. 显示文本文件mydoc匹配(含有)字符串"sun"的所有行  $awk '/sun/{print}' mydoc 3.由于显示整个记录(全行)是awk的缺省动作,因此可以省略action项  $awk '/sun/' mydoc 4.示第一个匹配Sun或sun的行与第一个匹配Moon或moon的行之间的行,并显示到标准输出上:  $awk '/[Ss]un/,/[Mm]oon/ {print}' myfile 5.下面的示例显示了内置变量和内置函数length()的使用:  $awk 'length($0)>80 {print NR}' myfile 6.UNIX中的用户进行安全性检查。  方法是考察/etc下的passwd文件,检查其中的passwd字段(第二字段)是否为为"*"  如不为"*",则表示该用户没有设置密码,显示出这些用户名(第一字段)。  #awk -F: '$2=="" {printf("%s no password!\n",$1' /etc/passwd  在这个示例中,passwd文件的字段分隔符是“:”,因此,必须用-F:来更改默认的字段分隔符,  这个示例中也涉及到了内置函数printf的使用

一、算术函数:

以下算术函数执行与 C 语言中名称相同的子例程相同的操作:

函数名说明atan2( y, x )返回 y/x 的反正切。cos( x )返回 x 的余弦;x 是弧度。sin( x )返回 x 的正弦;x 是弧度。exp( x )返回 x 幂函数。log( x )返回 x 的自然对数。sqrt( x )返回 x 平方根。int( x )返回 x 的截断至整数的值。rand( )返回任意数字 n,其中 0 <= n < 1。srand( [Expr] )将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。

举例说明:[chengmo@centos5 ~]$ awk 'BEGIN{OFMT="%.3f";fs=sin(1);fe=exp(10);fl=log(10);fi=int(3.1415);print fs,fe,fl,fi;}'0.841 22026.466 2.303 3OFMT 设置输出数据格式是保留3位小数获得随机数:[chengmo@centos5 ~]$ awk 'BEGIN{srand();fr=int(100*rand());print fr;}'78[chengmo@centos5 ~]$ awk 'BEGIN{srand();fr=int(100*rand());print fr;}'31[chengmo@centos5 ~]$ awk 'BEGIN{srand();fr=int(100*rand());print fr;}'41

二、字符串函数:


函数说明gsub( Ere, Repl, [ In ] )除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行,。sub( Ere, Repl, [ In ] )用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere 参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录($0 记录变量)。index( String1, String2 )在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。length [(String)]返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。blength [(String)]返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。substr( String, M, [ N ] )返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。match( String, Ere )在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。split( String, A, [Ere] )将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere 参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。tolower( String )返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。toupper( String )返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。sprintf(Format, Expr, Expr, . . . )根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。

Ere都可以是正则表达式

gsub,sub使用[chengmo@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}'   this is a test!test!在 info中查找满足正则表达式,/[0-9]+/ 用””替换,并且替换后的值,赋值给info 未给info值,默认是$0查找字符串(index使用)[wangsl@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}'    ok未找到,返回0正则表达式匹配查找(match使用)[wangsl@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";print match(info,/[0-9]+/)?"ok":"no found";}'           ok截取字符串(substr使用)[wangsl@centos5 ~]$ awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}'                         s is a tes从第 4个 字符开始,截取10个长度字符串 字符串分割(split使用)[chengmo@centos5 ~]$ awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}'44 test1 this2 is3 a分割info,动态创建数组tA,这里比较有意思,awk for …in 循环,是一个无序的循环。 并不是从数组下标1…n ,因此使用时候需要注意。

三、一般函数是:

函数说明close( Expression )用同一个带字符串值的 Expression 参数来关闭由 print 或 printf 语句打开的或调用 getline 函数打开的文件或管道。如果文件或管道成功关闭,则返回 0;其它情况下返回非零值。如果打算写一个文件,并稍后在同一个程序中读取文件,则 close 语句是必需的。system(Command )执行 Command 参数指定的命令,并返回退出状态。等同于system 子例程。Expression | getline [ Variable ]从来自 Expression 参数指定的命令的输出中通过管道传送的流中读取一个输入记录,并将该记录的值指定给 Variable 参数指定的变量。如果当前未打开将 Expression 参数的值作为其命令名称的流,则创建流。创建的流等同于调用 popen 子例程,此时 Command 参数取 Expression 参数的值且 Mode 参数设置为一个是 r 的值。只要流保留打开且 Expression 参数求得同一个字符串,则对 getline 函数的每次后续调用读取另一个记录。如果未指定 Variable 参数,则 $0 记录变量和 NF 特殊变量设置为从流读取的记录。getline [ Variable ] < Expression从 Expression 参数指定的文件读取输入的下一个记录,并将 Variable 参数指定的变量设置为该记录的值。只要流保留打开且 Expression 参数对同一个字符串求值,则对 getline 函数的每次后续调用读取另一个记录。如果未指定 Variable 参数,则 $0 记录变量和 NF 特殊变量设置为从流读取的记录。getline [ Variable ]将 Variable 参数指定的变量设置为从当前输入文件读取的下一个输入记录。如果未指定 Variable 参数,则 $0 记录变量设置为该记录的值,还将设置 NF、NR 和 FNR 特殊变量。

打开外部文件(close用法)[chengmo@centos5 ~]$ awk 'BEGIN{while("cat /etc/passwd"|getline){print $0;};close("/etc/passwd");}'root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin逐行读取外部文件(getline使用方法)[chengmo@centos5 ~]$ awk 'BEGIN{while(getline < "/etc/passwd"){print $0;};close("/etc/passwd");}'root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin[chengmo@centos5 ~]$ awk 'BEGIN{print "Enter your name:";getline name;print name;}'Enter your name:chengmochengmo调用外部应用程序(system使用方法)[chengmo@centos5 ~]$ awk 'BEGIN{b=system("ls -al");print b;}'total 42092drwxr-xr-x 14 chengmo chengmo     4096 09-30 17:47 .drwxr-xr-x 95 root   root       4096 10-08 14:01 ..b返回值,是执行结果。

四、时间函数

函数名说明mktime( YYYY MM DD HH MM SS[ DST])生成时间格式strftime([format [, timestamp]])格式化时间输出,将时间戳转为时间字符串 
具体格式,见下表.systime()得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数

创建指定时间(mktime使用)[chengmo@centos5 ~]$ awk 'BEGIN{tstamp=mktime("2001 01 01 12 12 12");print strftime("%c",tstamp);}'2001年01月01日 星期一 12时12分12秒[chengmo@centos5 ~]$ awk 'BEGIN{tstamp1=mktime("2001 01 01 12 12 12");tstamp2=mktime("2001 02 01 0 0 0");print tstamp2-tstamp1;}'2634468求2个时间段中间时间差,介绍了strftime使用方法[chengmo@centos5 ~]$ awk 'BEGIN{tstamp1=mktime("2001 01 01 12 12 12");tstamp2=systime();print tstamp2-tstamp1;}' 308201392

strftime日期和时间格式说明符

格式描述%a星期几的缩写(Sun)%A星期几的完整写法(Sunday)%b月名的缩写(Oct)%B月名的完整写法(October)%c本地日期和时间%d十进制日期%D日期 08/20/99%e日期,如果只有一位会补上一个空格%H用十进制表示24小时格式的小时%I用十进制表示12小时格式的小时%j从1月1日起一年中的第几天%m十进制表示的月份%M十进制表示的分钟%p12小时表示法(AM/PM)%S十进制表示的秒%U十进制表示的一年中的第几个星期(星期天作为一个星期的开始)%w十进制表示的星期几(星期天是0)%W十进制表示的一年中的第几个星期(星期一作为一个星期的开始)%x重新设置本地日期(08/20/99)%X重新设置本地时间(12:00:00)%y两位数字表示的年(99)%Y当前月份%Z时区(PDT)%%百分号(%)

以上是awk常见 内置函数使用及说明,希望对大家有所帮助。

awk中的自定义函数

原始的awk并不提供函数功能,只有在nawk或较新的awk版本中才可以增加函数
awk函数的定义方法如下:

function 函数名(参数表){     函数体 } 

        在gawk中允许将function省略为func,但其它版本的awk不允许。在 awk中调用函数比较简单,其方法与C语言相似,但awk比C语言更为灵活,它不执行参数有效性检查。换句话说,在你调用函数时,可以列出比函数预计(函 数定义中规定)的多或少的参数,多余的参数会被awk所忽略,而不足的参数,awk将它们置为缺省值0或空字符串,具体置为何值,将取决于参数的使用方 式。在函数中使用形如:return 返回值 格式的语句。

例: 下面的例子演示了函数的使用。在这个示例中,定义了一个名为print_header的函数,该函数调用了两个参数FileName和 PageNum,FileName参数传给函数当前使用的文件名,PageNum参数是当前页的页号。这个函数的功能是打印(显示)出当前文件的文件名, 和当前页的页号。完成这个功能后,这个函数将返回下一页的页号。

nawk >'BEGIN{pageno=1;file=FILENAME >pageno=print_header(file,pageno);#调用函数print_header >printf("当前页页号是:%d\n",pageno); >} >#定义函数print_header >function print_header(FileName,PageNum){ >printf("%s %d\n",FileName,PageNum); >PageNum++;return PageNUm; >} >}' myfile 执行这个程序将显示如下内容: myfile 1 当前页页号是:2 

NR与FNR:
QUOTE:
A.awk对多输入文件的执行顺序是,先将代码作用于第一个文件(一行行读入),然后该重复的代码又作用于第二个文件,再作用于第三个文件。
B.awk对多输入文件的执行顺序产生了行序号的问题。当第一个文件执行完,下次读入第二个文件,那么第二个文件的第一行怎么算呢?如果又计为1的话,那不就两个1了么?(因为第一个文件也有第一行)。这就是NR和FNR的问题。
   NR :全局行数(第二个文件的第一行接着第一个文件尾行数顺序计数)
   FNR:当前文件自身的行数(不考虑前几个输入文件的自身行数及总数)
         例如:data1.txt中有40行,data2.txt中有50行,那么awk ‘{}’ data1.txt data2.txt
                  NR  的值依次为:1,2……40,41,42……90
                   FNR的值依次为:1,2……40, 1, 2……50 
getline函数说明:
awk 的 getline语句用于简单地读取一条记录。如果用户有一个数据记录类似两个物理记录,那么getline将尤其有用。它完成一般字段的分离(设置字段变量$0 FNR NF NR)。如果成功则返回1,失败则返回0(到达文件尾)。
QUOTE:
A.getline从整体上来说,应这么理解它的用法:
            当其左右无重定向符 | 或 < 时,getline作用于当前文件,读入当前文件的第一行给其后跟的变量 
            var 或$0(无变量);应该注意到,由于awk在处理getline之前已经读入了一行,所以getline得到
            的返回结果是隔行的。
            当其左右有重定向符 | 或 < 时,getline则作用于定向输入文件,由于该文件是刚打开,并没有被
            awk读入一行,只是getline读入,那么getline返回的是该文件的第一行,而不是隔行。
B.getline用法大致可分为三大类(每大类又分两小类),即总共有6种用法。代码如下:
QUOTE:
nawk ‘BEGIN{“cat data.txt”|getline d; print d}’ data2.txt 
nawk ‘BEGIN{“cat data.txt”|getline; print $0}’ data2.txt
nawk ‘BEGIN{getline d < “data.txt”; print d}’ data2.txt 
nawk ‘BEGIN{getline < “data.txt”; print $0}’ data2.txt
      以上四行代码均实现“只打印data.txt文件的第一行”(若打印全部行,用循环)
eg. nawk ‘BEGIN{FS=”:”;while(getline<”/etc/passwd”>0){print $1}}’ data.txt

QUOTE:
nawk ‘{getline d; print d”#”$3}’ data.txt
 awk首先读入第一行,接着处理getline函数,然后把下一行指定给变量d,再先打印d,由于d后面有换行符,所以后面紧跟的#会覆盖d,后面的$3同样也会覆盖d。
QUOTE:
nawk ‘{getline; print $0”#”$3}’ data.txt
awk首先读入第一行接着处理getline函数,然后把下一行指定给$0,现在的$0已经是下一行内容,后面的#和$3(从$0中取)会覆盖$0的内容。
在awk中,有时需要调用系统工具来完成awk不擅长的工作,awk提供的system命令可以用来执行,但收不到外部工具的输出结果。好在可以运用getline来满足这个需求。例如
test.awk:
{
   datecommand="/bin/date -j -f \"%d/%b/%Y:%H:%M:%S\" " $olddatestr " \"+%Y%m%d %H%M%S\"";
   datecommand | getline newdatestr 
   close(datecommand);
}
 
外部命令需要awk占用一个文件描述符,而awk最多能打开的文件有一个上限,而且不大(比如说16),所以最后做一个close是好习惯。把命令串定义为一个变量也是为了close的时候方便

awk高级输入输出

1.读取下一条记录: 
        awk的next语句导致awk读取下一个记录并完成模式匹配,然后立即执行相应的操作。通常它用匹配的模式执行操作中的代码。next导致这个记录的任何额外匹配模式被忽略。 

2.简单地读取一条记录 
        awk 的 getline语句用于简单地读取一条记录。如果用户有一个数据记录类似两个物理记录,那么getline将尤其有用。它完成一般字段的分离(设置字段变 量$0 FNR NF NR)。如果成功则返回1,失败则返回0(到达文件尾)。如果需简单地读取一个文件,则可以编写以下代码: 
例:示例getline的使用 
{while(getline==1) 

#process the inputted fields 


也可以使getline保存输入数据在一个字段中,而不是通过使用getline variable的形式处理一般字段。当使用这种方式时,NF被置成0,FNR和NR被增值。 
用 户也可以使用getline<"filename"方式从一个给定的文件中输入数据,而不是从命令行所列内容输入数据。此时,getline将完成 一般字段分离(设置字段变量$0和NF)。如果文件不存在,返回-1,成功,返回1,返回0表示失败。用户可以从给定文件中读取数据到一个变量中,也可以 用stdin(标准输入设备)或一个包含这个文件名的变量代替filename。值得注意的是当使用这种方式时不修改FNR和NR。 

另一种使用getline语句的方法是从UNIX命令接受输入,例如下面的例子: 
例:示例从UNIX命令接受输入 
{while("who -u"|getline) 

#process each line from the who command 



当然,也可以使用如下形式: 
"command" | getline variable 

3.关闭文件: 
        awk中允许在程序中关闭一个输入或输出文件,方法是使用awk的close语句。 
close("filename") 
filename可以是getline打开的文件(也可以是stdin,包含文件名的变量或者getline使用的确切命令)。或一个输出文件(可以是stdout,包含文件名的变量或使用管道的确切命令)。 

4.输出到一个文件: 
        awk中允许用如下方式将结果输出到一个文件: 
printf("hello word!\n")>"datafile" 
或 
printf("hello word!\n")>>"datafile" 

5.输出到一个命令 
        awk中允许用如下方式将结果输出到一个命令: 
printf("hello word!\n")|"sort-t','"

awk与shell script混合编程

因 为awk可以作为一个shell命令使用,因此awk能与shell批处理程序很好的融合在一起,这给实现awk与shell程序的混合编程提供了可能。 实现混合编程的关键是awk与shell script之间的对话,换言之,就是awk与shell script之间的信息交流:awk从shell script中获取所需的信息(通常是变量的值)、在awk中执行shell命令行、shell script将命令执行的结果送给awk处理以及shell script读取awk的执行结果等等。 

1.awk读取Shell script程序变量 
在awk中我们可以通过“'$变量名'”的方式读取sell scrpit程序中的变量。 
例:在下面的示例中,我们将读取sell scrpit程序中的变量Name,该变量存放的是文本myfile的撰写者,awk将打印出这个人名。 
$cat writename 

# @(#) 




Name="张三" nawk 'BEGIN {name="'Name'";\ printf("\t%s\t撰写者%s\n",FILENAME,name");}\ 
{...}END{...}' myfile 




2.将shell命令的执行结果送给awk处理 
作为信息传送的一种方法,我们可以将一条shell命令的结果通过管道线(|)传递给awk处理: 
例:示例awk处理shell命令的执行结果 
$who -u | awk '{printf("%s正在执行%s\n",$2,$1)}' 
该命令将打印出注册终端正在执行的程序名。 

3.shell script程序读awk的执行结果 
为 了实现shell script程序读取awk执行的结果,我们可以采取一些特殊的方法,例如我们可以用变量名=`awk语句`的形式将awk执行的结果存放入一个 shell script变量。当然也可以用管道线的方法将awk执行结果传递给shell script程序处理。 
例:作为传送消息 的机制之一,UNIX提供了一个向其所有用户传送消息的命令wall(意思是write to all写给所有用户),该命令允许向所有工作中的用户(终端)发送消息。为此,我们可以通过一段shell批处理程序wall.shell来模拟这一程序 (事实上比较老的版本中wall就是一段shell批处理程序: 

$cat wall.shell 

# @(#) wall.shell:发送消息给每个已注册终端 

cat >/tmp/
#用户录入消息文本 who -u | awk '{print $2}' | while read tty  
do  
cat /tmp/
>$tty 
done 

在 这个程序里,awk接受who -u命令的执行结果,该命令打印出所有已注册终端的信息,其中第二个字段是已注册终端的设备名,因此用awk命令析出该设备名,然后用while read tty语句循环读出这些文件名到变量(shell script变量)tty中,作为信息传送的终结地址。 

4.在awk中执行shell命令行----嵌入函数system() 
system()是一个不适合字符或数字类型的嵌入函数,该函数的功能是处理作为参数传递给它的字符串。system对这个参数的处理就是将其作为命令处理,也就是说将其当作命令行一样加以执行。这使得用户在自己的awk程序需要时可以灵活地执行命令或脚本。 
例:下面的程序将使用system嵌入函数打印用户编制好的报表文件,这个文件存放在名为myreport.txt的文件中。为简约起见,我们只列出了其END部分: 



END {close("myreport.txt");system("lp myreport.txt");} 
在这个示例中,我们首先使用close语句关闭了文件myreport.txt文件,然后使用system嵌入函数将myreport.txt送入打印机打印。 

入门实例

假设last -n 5的输出如下[root@www ~]# last -n 5 <==仅取出前五行  root     pts/1   192.168.1.100  Tue Feb 10 11:21   still logged in  root     pts/1   192.168.1.100  Tue Feb 10 00:46 - 02:28  (01:41)  root     pts/1   192.168.1.100  Mon Feb  9 11:41 - 18:30  (06:48)  dmtsai   pts/1   192.168.1.100  Mon Feb  9 11:41 - 11:41  (00:00)  root     tty1                   Fri Sep  5 14:09 - 14:10  (00:01)  如果只是显示最近登录的5个帐号#last -n 5 | awk  '{print $1}'  root  root  root  dmtsai  root    awk工作流程是这样的:读入有'\n'换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域。默认域分隔符是"空白键" 或 "[tab]键",所以$1表示登录用户,$3表示登录用户ip,以此类推。如果只是显示/etc/passwd的账户#cat /etc/passwd |awk  -F ':'  '{print $1}'    root  daemon  bin  sys   这种是awk+action的示例,每行都会执行action{print $1}。-F指定域分隔符为':'。如果只是显示/etc/passwd的账户和账户对应的shell,而账户与shell之间以tab键分割 #cat /etc/passwd |awk  -F ':'  '{print $1"\t"$7}'  root    /bin/bash  daemon  /bin/sh  bin     /bin/sh  sys     /bin/s   如果只是显示/etc/passwd的账户和账户对应的shell,而账户与shell之间以逗号分割,而且在所有行添加列名name,shell,在最后一行添加"blue,/bin/nosh"。cat /etc/passwd |awk  -F ':'  'BEGIN {print "name,shell"}  {print $1","$7} END {print "blue,/bin/nosh"}'  name,shell  root,/bin/bash  daemon,/bin/sh  bin,/bin/sh  sys,/bin/sh  ....  blue,/bin/nosh   awk工作流程是这样的:先执行BEGING,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。搜索/etc/passwd有root关键字的所有行#awk -F: '/root/' /etc/passwd  root:x:0:0:root:/root:/bin/bash  这种是pattern的使用示例,匹配了pattern(这里是root)的行才会执行action(没有指定action,默认输出每行的内容)。搜索支持正则,例如找root开头的: awk -F: '/^root/' /etc/passwd搜索/etc/passwd有root关键字的所有行,并显示对应的shell# awk -F: '/root/{print $7}' /etc/passwd               /bin/bash  这里指定了action{print $7}

应用1  awk -F: '{print NF}' helloworld.sh                   //输出文件每行有多少字段  awk -F: '{print $1,$2,$3,$4,$5}' helloworld.sh       //输出前5个字段  awk -F: '{print $1,$2,$3,$4,$5}' OFS='\t' helloworld.sh    //输出前5个字段并使用制表符分隔输出  awk -F: '{print NR,$1,$2,$3,$4,$5}' OFS='\t' helloworld.sh //制表符分隔输出前5个字段,并打印行号    应用2  awk -F'[:#]' '{print NF}'  helloworld.sh        //指定多个分隔符: #,输出每行多少字段  awk -F'[:#]' '{print $1,$2,$3,$4,$5,$6,$7}' OFS='\t' helloworld.sh //制表符分隔输出多字段    应用3  awk -F'[:#/]' '{print NF}' helloworld.sh     //指定三个分隔符,并输出每行字段数  awk -F'[:#/]' '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12}' helloworld.sh   //制表符分隔输出多字段    应用4  计算/home目录下,普通文件的大小,使用KB作为单位  ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",sum/1024,"KB"}'  ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",int(sum/1024),"KB"}'  //int是取整的意思    应用5  统计netstat -anp 状态为LISTEN和CONNECT的连接数量分别是多少  netstat -anp|awk '$6~/LISTEN|CONNECTED/{sum[$6]++} END{for (i in sum) printf "%-10s %-6s %-3s \n", i," ",sum[i]}'    应用6  统计/home目录下不同用户的普通文件的总数是多少?  ls -l|awk 'NR!=1 && !/^d/{sum[$3]++} END{for (i in sum) printf "%-6s %-5s %-3s \n",i," ",sum[i]}'     mysql        199   root           374   统计/home目录下不同用户的普通文件的大小总size是多少?  ls -l|awk 'NR!=1 && !/^d/{sum[$3]+=$5} END{for (i in sum) printf "%-6s %-5s %-3s %-2s \n",i," ",sum[i]/1024/1024,"MB"}'    应用7  输出成绩表  awk 'BEGIN{math=0;eng=0;com=0;printf "Lineno.   Name    No.    Math   English   Computer    Total\n";printf "------------------------------------------------------------\n"}{math+=$3; eng+=$4; com+=$5;printf "%-8s %-7s %-7s %-7s %-9s %-10s %-7s \n",NR,$1,$2,$3,$4,$5,$3+$4+$5} END{printf "------------------------------------------------------------\n";printf "%-24s %-7s %-9s %-20s \n","Total:",math,eng,com;printf "%-24s %-7s %-9s %-20s \n","Avg:",math/NR,eng/NR,com/NR}' test0    [root@localhost home]# cat test0   Marry   2143 78 84 77  Jack    2321 66 78 45  Tom     2122 48 77 71  Mike    2537 87 97 95  Bob     2415 40 57 62  

附录: 

1.awk的常规表达式元字符 
\ 换码序列 
^ 在字符串的开头开始匹配 
$ 在字符串的结尾开始匹配 
. 与任何单个字符串匹配 
[ABC] 与[]内的任一字符匹配 
[A-Ca-c] 与A-C及a-c范围内的字符匹配(按字母表顺序) 
[^ABC] 与除[]内的所有字符以外的任一字符匹配 
Desk|Chair 与Desk和Chair中的任一个匹配 
[ABC][DEF] 关联。与A、B、C中的任一字符匹配,且其后要跟D、E、F中的任一个字符。 
* 与A、B或C中任一个出现0次或多次的字符相匹配 
+ 与A、B或C中任何一个出现1次或多次的字符相匹配 
? 与一个空串或A、B或C在任何一个字符相匹配 
(Blue|Black)berry 合并常规表达式,与Blueberry或Blackberry相匹配 

2.awk算术运算符 
运算符 用途 
------------------ 
x^y x的y次幂 
x**y 同上 
x%y 计算x/y的余数(求模) 
x+y x加y 
x-y x减y 
x*y x乘y 
x/y x除y 
-y 负y(y的开关符号);也称一目减 
++y y加1后使用y(前置加) 
y++ 使用y值后加1(后缀加) 
--y y减1后使用y(前置减) 
y-- 使用后y减1(后缀减) 
x=y 将y的值赋给x 
x+=y 将x+y的值赋给x 
x-=y 将x-y的值赋给x 
x*=y 将x*y的值赋给x 
x/=y 将x/y的值赋给x x%=y 将x%y的值赋给x 
x^=y 将x^y的值赋给x 
x**=y 将x**y的值赋给x 

3.awk允许的测试: 
操作符 含义 
x==y x等于y 
x!=y x不等于y 
x>y x大于y 
x>=y x大于或等于y 
x<y x小于y 
x<=y x小于或等于y? 
x~re x匹配正则表达式re? 
x!~re x不匹配正则表达式re? 

4.awk的操作符(按优先级升序排列) 
= 、+=、 -=、 *= 、/= 、 %= 
|| 
&& 
> >= < <= == != ~ !~ 
xy (字符串连结,'x''y'变成"xy") 
+ - 
* / % 
++ -- 

5.awk内置变量(预定义变量) 

说明:表中v项表示第一个支持变量的工具(下同):A=awk,N=nawk,P=POSIX awk,G=gawk 
V 变量 含义 缺省值 
-------------------------------------------------------- 
N ARGC 命令行参数个数 
G ARGIND 当前被处理文件的ARGV标志符 
N ARGV 命令行参数数组 
G CONVFMT 数字转换格式 %.6g 
P ENVIRON UNIX环境变量 
N ERRNO UNIX系统错误消息 
G FIELDWIDTHS 输入字段宽度的空白分隔字符串 
A FILENAME 当前输入文件的名字 
P FNR 当前记录数 
A FS 输入字段分隔符 空格 
G IGNORECASE 控制大小写敏感0(大小写敏感) 
A NF 当前记录中的字段个数 
A NR 已经读出的记录数 
A OFMT 数字的输出格式 %.6g 
A OFS 输出字段分隔符 空格 
A ORS 输出的记录分隔符 新行 
A RS 输入的记录他隔符 新行 
N RSTART 被匹配函数匹配的字符串首 
N RLENGTH 被匹配函数匹配的字符串长度 
N SUBSEP 下标分隔符 "\034" 

6.awk的内置函数 
V 函数 用途或返回值 
------------------------------------------------ 
N gsub(reg,string,target) 每次常规表达式reg匹配时替换target中的string 
N index(search,string) 返回string中search串的位置 
A length(string) 求串string中的字符个数 
N match(string,reg) 返回常规表达式reg匹配的string中的位置 
N printf(format,variable) 格式化输出,按format提供的格式输出变量variable。 
N split(string,store,delim) 根据分界符delim,分解string为store的数组元素 
N sprintf(format,variable) 返回一个包含基于format的格式化数据,variables是要放到串中的数据 
G strftime(format,timestamp) 返回一个基于format的日期或者时间串,timestmp是systime()函数返回的时间 
N sub(reg,string,target) 第一次当常规表达式reg匹配,替换target串中的字符串 
A substr(string,position,len) 返回一个以position开始len个字符的子串 
P totower(string) 返回string中对应的小写字符 
P toupper(string) 返回string中对应的大写字符 
A atan(x,y) x的余切(弧度) 
N cos(x) x的余弦(弧度) 
A exp(x) e的x幂 
A int(x) x的整数部分 
A log(x) x的自然对数值 
N rand() 0-1之间的随机数 
N sin(x) x的正弦(弧度) 
A sqrt(x) x的平方根 
A srand(x) 初始化随机数发生器。如果忽略x,则使用system() 
G system() 返回自1970年1月1日以来经过的时间(按秒计算)




原创粉丝点击