0.awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk。 1.awk语言的最基本功能是在文件或字符串中基于指定规则来分解抽取信息,也可以基于指定的规则来输出数据。完整的awk脚本通常用来格式化文本文件中的信息。 2.三种方式调用awk 1) 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设置变量 2) 将awk_script放入脚本文件并以 #!/bin/awk -f 作为首行,给予该脚本可执行权限,然后在shell下通过键入该脚本的脚本名调用之。 3) 将所有的awk_script插入一个单独脚本文件,然后调用: awk -f awk脚本文件 input_file(s) 3. awk的运行过程 1) awk_script的组成: ① awk_script可以由一条或多条awk_cmd组成,两条awk_cmd之间一般以NEWLINE分隔 ② awk_cmd由两部分组成: awk_pattern { actions } ③ awk_script可以被分成多行书写,必须确保整个awk_script被单引号括起来。 2) awk命令的一般形式: awk ' BEGIN { actions } awk_pattern1 { actions } ............ awk_patternN { actions } END { actions } ' inputfile 其中 BEGIN { actions } 和 END { actions } 是可选的。 3) awk的运行过程: ① 如果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。 4) iput_file可以是多于一个文件的文件列表,awk将按顺序处理列表中的每个文件。 5) 一条awk_cmd的awk_pattern可以省略,省略时不对输入记录进行匹配比较就执行相应的actions。一条awk_cmd的actions 也可以省略,省略时默认的动作为打印当前输入记录(print $0) 。一条awk_cmd中的awk_pattern和actions不能同时省略。 6) BEGIN区块和END区块别位于awk_script的开头和结尾。awk_script中只有END区块或者只有BEGIN区块是被允许的。如果awk_script中只有BEGIN { actions } ,awk不会读取input_file。 7) awk把输入文件的数据读入内存,然后操作内存中的输入数据副本,awk不会修改输入文件的内容。 8) awk的总是输出到标准输出,如果想让awk输出到文件,可以使用重定向。 4.awk_pattern awk_pattern模式部分决定actions动作部分何时触发及触发actions。awk_pattern可以是以下几种类型: 1) 正则表达式用作awk_pattern: /regexp/ ① awk中正则表达式匹配操作中经常用到的字符: \ ^ $ . [] | () * // 通用的regexp元字符 + : 匹配其前的单个字符一次以上,是awk自有的元字符,不适用于grep或sed等 ? : 匹配其前的单个字符1次或0次,是awk自有的元字符,不适用于grep或sed等 ② 举例: awk '/ *\$0\.[0-9][0-9].*/' input_file 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 "ok"}' input_file awk '/^d/ || /x$/ {print "ok"}' input_file ④ 其它表达式用作awk_script,如赋值表达式等 eg: awk '(tot+=$6); END{print "total points :" tot }' input_file // 分号不能省略 awk 'tot+=$6 {print $0} END{print "total points :" tot }' input_file // 与上面等效 awk 用法例举: 变量名 含义 ARGC 命令行变元个数 ARGV 命令行变元数组 FILENAME 当前输入文件名 FNR 当前文件中的记录号 FS 输入域分隔符,默认为一个空格 RS 输入记录分隔符 NF 当前记录里域个数 NR 到目前为止记录数 OFS 输出域分隔符 ORS 输出记录分隔符 1、awk '/101/' file 显示文件file中包含101的匹配行。 awk '/101/,/105/' file ###包含101或者105的行 awk '$1 == 5' file awk '$1 == "CT"' file 注意必须带双引号 awk '$1 * $2 >100 ' file awk '$2 >5 && $2<=15' file 2、awk '{print NR,NF,$1,$NF,}' file 显示文件file的当前记录号、域数和每一行的第一个和最后一个域。 awk '/101/ {print $1,$2 + 10}' file 显示文件file的匹配行的第二个域加10。 awk '/101/ {print $1$2}' file awk '/101/ {print $1 $2}' file 显示文件file的匹配行的第一、二个域,但显示时域中间没有分隔符。 3、df | awk '$4>1000000 ' 通过管道符获得输入,如:显示第4个域满足条件的行。 4、awk -F "|" '{print $1}' file 按照新的分隔符“|”进行操作。 awk 'BEGIN { FS="[: \t|]" } {print $1,$2,$3}' file 通过设置输入分隔符(FS="[: \t|]")修改输入分隔符。 Sep="|" awk -F $Sep '{print $1}' file 按照环境变量Sep的值做为分隔符。 awk -F '[ :\t|]' '{print $1}' file 按照正则表达式的值做为分隔符,这里代表空格、:、TAB、|同时做为分隔符。 awk -F '[][]' '{print $1}' file 按照正则表达式的值做为分隔符,这里代表[、] 5、awk -f awkfile file 通过文件awkfile的内容依次进行控制。 cat awkfile /101/{print "\047 Hello! \047"} --遇到匹配行以后打印 ' Hello! '.\047代表单引号。 {print $1,$2} --因为没有模式控制,打印每一行的前两个域。 6、awk '$1 ~ /101/ {print $1}' file 显示文件中第一个域匹配101的行(记录)。 7、awk 'BEGIN { OFS="%"} {print $1,$2}' file 通过设置输出分隔符(OFS="%")修改输出格式。 8、awk 'BEGIN { max=100 ;print "max=" max} BEGIN 表示在处理任意行之前进行的操作。 {max=($1 >max ?$1:max); print $1,"Now max is "max}' file 取得文件第一个域的最大值。 (表达式1?表达式2:表达式3 相当于: if (表达式1) 表达式2 else 表达式3 awk '{print ($1>4 ? "high "$1: "low "$1)}' file 9、awk '$1 * $2 >100 {print $1}' file 显示文件中第一个域匹配101的行(记录)。 10、awk '{$1 == 'Chi' {$3 = 'China'; print}' file 找到匹配行后先将第3个域替换后再显示该行(记录)。 awk '{$7 %= 3; print $7}' file 将第7域被3除,并将余数赋给第7域再打印。 11、awk '/tom/ {wage=$2+$3; printf wage}' file 找到匹配行后为变量wage赋值并打印该变量。 12、awk '/tom/ {count++;} END {print "tom was found "count" times"}' file END表示在所有输入行处理完后进行处理。 13、awk 'gsub(/\$/,"");gsub(/,/,""); cost+=$4; END {print "The total is $" cost>"filename"}' file gsub函数用空串替换$和,再将结果输出到filename中。 1 2 3 $1,200.00 1 2 3 $2,300.00 1 2 3 $4,000.00 awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>1000&&$4<2000) c1+=$4; else if ($4>2000&&$4<3000) c2+=$4; else if ($4>3000&&$4<4000) c3+=$4; else c4+=$4; } END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 通过if和else if完成条件语句 awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>3000&&$4<4000) exit; else c4+=$4; } END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 通过exit在某条件时退出,但是仍执行END操作。 awk '{gsub(/\$/,"");gsub(/,/,""); if ($4>3000) next; else c4+=$4; } END {printf "c4=[%d]\n",c4}"' file 通过next在某条件时跳过该行,对下一行执行操作。 14、awk '{ print FILENAME,$0 }' file1 file2 file3>fileall 把file1、file2、file3的文件内容全部写到fileall中,格式为 打印文件并前置文件名。 15、awk ' $1!=previous { close(previous); previous=$1 } {print substr($0,index($0," ") +1)>$1}' fileall 把合并后的文件重新分拆为3个文件。并与原文件一致。 16、awk 'BEGIN {"date"|getline d; print d}' 通过管道把date的执行结果送给getline,并赋给变量d,然后打印。 17、awk 'BEGIN {system("echo "Input your name:\\c""); getline d;print "\nYour name is",d,"\b!\n"}' 通过getline命令交互输入name,并显示出来。 awk 'BEGIN {FS=":"; while(getline< "/etc/passwd" >0) { if($1~"050[0-9]_") print $1}}' 打印/etc/passwd文件中用户名包含050x_的用户名。 18、awk '{ i=1;while(i<NF) {print NF,$i;i++}}' file 通过while语句实现循环。 awk '{ for(i=1;i<NF;i++) {print NF,$i}}' file 通过for语句实现循环。 type file|awk -F "/" ' { for(i=1;i<NF;i++) { if(i==NF-1) { printf "%s",$i } else { printf "%s/",$i } }}' 显示一个文件的全路径。 用for和if显示日期 awk 'BEGIN { for(j=1;j<=12;j++) { flag=0; printf "\n%d月份\n",j; for(i=1;i<=31;i++) { if (j==2&&i>28) flag=1; if ((j==4||j==6||j==9||j==11)&&i>30) flag=1; if (flag==0) {printf "%02d%02d ",j,i} } } }' 19、在awk中调用系统变量必须用单引号,如果是双引号,则表示字符串 Flag=abcd awk '{print '$Flag'}' 结果为abcd awk '{print "$Flag"}' 结果为$Flag 6. 打印报告头 上述命令输出在名字和腰带级别之间用一些空格使之更容易划分,也可以在域间使用t a b键加以划分。为加入t a b键,使用t a b键速记引用符\ t,后面将对速记引用加以详细讨论。也可以为输出文本加入信息头。本例中加入n a m e和b e l t及下划线。下划线使用\ n,强迫启动新行,并在\ n下一行启动打印文本操作。打印信息头放置在B E G I N模式部分,因为打印信息头被界定为一个动作,必须用大括号括起来。在a w k查看第一条记录前,信息头被打印。 $ awk 'BEGIN {print "Name Belt\n-----------------------------------"}{print $1"\t",$4}' grade.txt Name Belt ----------------------------------- M.Tans Green J.Lulu green P.Bunny Yellow J.Troll Brown-3 L.Tansl Brown-2 复制代码 7. 打印信息尾 如果在末行加入end of report信息,可使用E N D语句。E N D语句在所有文本处理动作执行完之后才被执行。E N D语句在脚本中的位置放置在主要动作之后。下面简单打印头信息并告之查询动作完成。 $ awk 'BEGIN {print "Name\n--------"}{print $1} END {print "end-of-report"}' grade.txt Name -------- M.Tans J.Lulu P.Bunny J.Troll L.Tansl 复制代码 8. awk错误信息提示 几乎可以肯定,在使用a w k时,将会在命令中碰到一些错误。a w k将试图打印错误行,但由于大部分命令都只在一行,因此帮助不大。 系统给出的显示错误信息提示可读性不好。使用上述例子,如果丢了一个双引号, a w k将返回: $ awk 'BEGIN {print "Name\n--------}{print $1} END {"end-of-report"}' grade.txt awk: cmd. line:1: BEGIN {print "Name\n--------}{print $1} END {"end-of-report"} awk: cmd. line:1: ^ unterminated string 复制代码 确保整个a w k命令用单引号括起来。 " 确保命令内所有引号成对出现。 " 确保用花括号括起动作语句,用圆括号括起条件语句。 " 可能忘记使用花括号,也许你认为没有必要,但a w k不这样认为,将按之解释语法 1.为使一域号匹配正则表达式,使用符号‘~’后紧跟正则表达式,也可以用i f语句。a w k中i f后面的条件用()括起来。 观察文件g r a d e . t x t,如果只要显示b r o w n腰带级别可知其所在域为f i e l d - 4,这样可以写出表达式{if($4~/brown/) print }意即如果f i e l d - 4包含b r o w n,打印它。如果条件满足,则打印匹配记录行。可以编写下面脚本,因为这是一个动作,必须用花括号{ }括起来。 [root@Linux_chenwy sam]# awk '{if($4~/Brown/) print $0}' grade.txt J.Troll 07/99 4842 Brown-3 12 26 26 L.Tansl 05/99 4712 Brown-2 12 30 28 2. 精确匹配 为精确匹配4 8,使用等号= =,并用单引号括起条件。例如$ 3 [root@Linux_chenwy sam]# awk '$3=="48" {print$0}' grade.txt P.Bunny 02/99 48 Yellow 12 35 28 [root@Linux_chenwy sam]# awk '{if($3=="48") print$0}' grade.txt P.Bunny 02/99 48 Yellow 12 35 28 复制代码 3. 不匹配 有时要浏览信息并抽取不匹配操作的记录,与~相反的符号是!~,意即不匹配。像原来使用查询b r o w n腰带级别的匹配操作一样,现在看看不匹配情况。表达式$0 !~/brown/,意即查询不包含模式b r o w n腰带级别的记录并打印它。 注意,缺省情况下, a w k将打印所有匹配记录,因此这里不必加入动作部分。 [root@Linux_chenwy sam]# awk '$0 !~ /Brown/' grade.txt M.Tans 5/99 48311 Green 8 40 44 J.Lulu 06/99 48317 green 9 24 26 P.Bunny 02/99 48 Yellow 12 35 28 复制代码 如果只使用命令awk$4 !="brown"{print $0} grade.txt,将返回错误结果,因为用引号括起了b r o w n,将只匹配‘b r o w n而不匹配b r o w n - 2和b r o w n - 3,当然,如果想要查询非b r o w n - 2的腰带级别,可做如下操作: [root@Linux_chenwy sam]# awk '$4!="Brown-2" {print $0}' grade.txt M.Tans 5/99 48311 Green 8 40 44 J.Lulu 06/99 48317 green 9 24 26 P.Bunny 02/99 48 Yellow 12 35 28 J.Troll 07/99 4842 Brown-3 12 26 26 复制代码 4. 小于 看看哪些学生可以获得升段机会。测试这一点即判断目前级别分f i e l d - 6是否小于最高分f i e l d - 7,在输出结果中,加入这一改动很容易。 [root@Linux_chenwy sam]# awk '{if($6 < $7) print $0}' grade.txt M.Tans 5/99 48311 Green 8 40 44 J.Lulu 06/99 48317 green 9 24 26 复制代码 5. 小于等于 对比小于,小于等于只在操作符上做些小改动,满足此条件的记录也包括上面例子中的输出情况。 [root@Linux_chenwy sam]# awk '{if($6 <= $7) print $1}' grade.txt M.Tans J.Lulu J.Troll 复制代码 6. 大于 [root@Linux_chenwy sam]# awk '{if($6 > $7) print $1}' grade.txt P.Bunny L.Tansl 复制代码 7. 设置大小写 为查询大小写信息,可使用[ ]符号。在测试正则表达式时提到可匹配[ ]内任意字符或单词,因此若查询文件中级别为g r e e n的所有记录,不论其大小写,表达式应为‘ / [ G g ] r e e n /’ [root@Linux_chenwy sam]# awk '/[Gg]reen/' grade.txt M.Tans 5/99 48311 Green 8 40 44 J.Lulu 06/99 48317 green 9 24 26 复制代码 8. 任意字符 抽取名字,其记录第一域的第四个字符是a,使用句点.。表达式/ ^ . . . a /意为行首前三个字符任意,第四个是a,尖角符号代表行首。 [root@Linux_chenwy sam]# awk '$1 ~ /^...a/' grade.txt M.Tans 5/99 48311 Green 8 40 44 L.Tansl 05/99 4712 Brown-2 12 30 28 复制代码 9. 或关系匹配 为抽取级别为y e l l o w或b r o w n的记录,使用竖线符|。意为匹配| 两边模式之一。注意,使用竖线符时,语句必须用圆括号括起来。 [root@Linux_chenwy sam]# awk '$0 ~/(Yellow|Brown)/' grade.txt P.Bunny 02/99 48 Yellow 12 35 28 J.Troll 07/99 4842 Brown-3 12 26 26 L.Tansl 05/99 4712 Brown-2 12 30 28 复制代码 上面例子输出所有级别为Ye l l o w或B r o w n的记录。 使用这种方法在查询级别为G r e e n或g r e e n时,可以得到与使用[ ]表达式相同的结果。 [root@Linux_chenwy sam]# awk '/^M/' grade.txt M.Tans 5/99 48311 Green 8 40 44 复制代码 10. 行首 不必总是使用域号。如果查询文本文件行首包含M的代码,可简单使用下面^符号: [root@Linux_chenwy sam]# awk '/^M/' grade.txt 复制代码 复合表达式即为模式间通过使用下述各表达式互相结合起来的表达式: && AND : 语句两边必须同时匹配为真。 || O R:语句两边同时或其中一边匹配为真。 ! 非求逆 11. AND 打印记录,使其名字为‘ P. B u n n y且级别为Ye l l o w,使用表达式( $ 1 = = " P. B u n n y " & &$ 4 = = " Ye l l o w " ),意为& &两边匹配均为真。完整命令如下: [root@Linux_chenwy sam]# awk '{if ($1=="P.Bunny" && $4=="Yellow") print $0}' grade.txt P.Bunny 02/99 48 Yellow 12 35 28 复制代码 12. Or 如果查询级别为Ye l l o w或B r o w n,使用或命令。意为“ | |”符号两边的匹配模式之一或全部为真。(Deven:注意{}是在最外围,不是这样写:'if ($4=="Yellow" || $4~/Brown/) {print $0}' [root@Linux_chenwy sam]# awk '{if ($4=="Yellow" || $4~/Brown/) print $0}' grade.txt P.Bunny 02/99 48 Yellow 12 35 28 J.Troll 07/99 4842 Brown-3 12 26 26 L.Tansl 05/99 4712 Brown-2 12 30 28 复制代码 原来不一定得加print,下面我自己对例一二做了一下 1 [root@Linux_chenwy sam]# awk '$4~/Brown/' grade.txt J.Troll 07/99 4842 Brown-3 12 26 26 L.Tansl 05/99 4712 Brown-2 12 30 28 复制代码 2 [root@Linux_chenwy sam]# awk '$3=="48"' grade.txt P.Bunny 02/99 48 Yellow 12 35 28 复制代码 [root@Linux_chenwy sam]# awk '$3="48"' grade.txt M.Tans 5/99 48 Green 8 40 44 J.Lulu 06/99 48 green 9 24 26 P.Bunny 02/99 48 Yellow 12 35 28 J.Troll 07/99 48 Brown-3 12 26 26 L.Tansl 05/99 48 Brown-2 12 30 28 复制代码 N F的一个强大功能是将变量$ P W D的返回值传入a w k并显示其目录。这里需要指,注意,NF与$NF是有区别的。定域分隔符/。 [root@chenwy sam]# echo $PWD | awk -F/ ' {print $NF}' sam 复制代码 另一个例子是显示文件名。 [root@chenwy sam]# echo "/usr/local/etc/rc.sybase" | awk -F/ '{print $NF}' rc.sybase 复制代码