首页 > 代码库 > AWK运用

AWK运用

awk:报告生成器,格式化文本输出
        有多种版本:New awk (nawk ),GNU awk(gawk)
gawk:模式扫描和处理语言
基本用法:
awk [options] ‘program’ var=value file…
awk [options] -f programfile var=value file…
awk [options] ‘BEGIN{ action;… } pattern{ action;… } END{action;… }’ file …
awk 程序通常由:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成
program 通常是被单引号或双引号;尽量使用单引号
  选项:
       -F  指明输入时用到的字段分隔符
       -v  var=value: 

基本格式:awk [options] ‘program’ file…
   program:pattern{action statements;..}
   pattern 和action: :
? pattern 部分决定动作语句何时触发及触发事件BEGIN,END
? action statements 对数据进行处理,放在{} 内指明print, printf分割符、域和记录
? awk 执行时,由分隔符分隔的字段(域)标记$1,$2..$n称
  为域标识。$0 为所有域,注意:和shell 中变量$ 符含义不同
? 文件的每一行称为记录
? 省略action行 ,则默认执行 print $0的操作

awk工作原理:
第一步:执行BEGIN{action;… } 语句块中的语句
第二步:从文件或标准输入(stdin) 读取一行,然后执行pattern{action;… } 语句块,
            它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{action;…}语句块BEGIN 语句块在awk开始从输
            入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表
           格的表头等语句通常可以写在BEGIN 语句块中
END 语句块在awk 从输入流中读取完所有的行之后即被执行,比如打印所有行的分析
      结果这类信息汇总都是在END 语句块中完成,它也是一个可选语句块
pattern 语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern 
            语句块,则默认执行{ print } ,即打印每一个读取到的行,
            awk 读取的每一行都会执行该语句块

 print 格式:print item1, item2, …
  要点:
? (1)  逗号分隔符
? (2)  输出的各item 可以字符串,也可以是数值;当前记录的字段、
变量或awk 的表达式
? (3)  如省略item ,相当于print $0
  示例:
awk ‘{print “hello,awk”}’  file
awk –F: ‘{print}’ /etc/passwd
awk –F: ‘{print “wang”}’ /etc/passwd
awk –F: ‘{print $1}’ /etc/passwd
awk –F: ‘{print $0}’ /etc/passwd
awk –F: ‘{print $1”\t”$3}’ /etc/passwd
tail –3 /etc/fstab |awk ‘{print $2,$4}’

awk变量:内置和自定义变量
 FS :输入字段分隔符,默认为空白字符
      awk -v FS=’:’ ‘{print $1,FS,$3}’ /etc/passwd
      awk –F: ‘{print $1,$3,$7}’ /etc/passwd
 OFS :输出字段分隔符,默认为空白字符
      awk -v FS=‘:’ -v OFS=‘:’ ‘{print $1,$3,$7}’ /etc/passwd
 RS :输入记录分隔符,指定输入时的换行符,原换行符仍有效
      awk -v RS=’ ‘ ‘{print }’ /etc/passwd
 ORS :输出记录分隔符,输出时用指定符号代替换行符
      awk -v RS=’ ‘ -v ORS=’###’‘{print }’ /etc/passwd
 NF :字段数量
      awk -F:‘{print NF}’ /etc/fstab, 引用内置变量不用$
      awk -F: ‘{print $(NF-1)}’ /etc/passwd
 NR :行号
      awk ‘{print NR}’ /etc/fstab ; awk END‘{print NR}’ /etc/fstab

FNR :各文件分别计数, 行号
    awk ‘{print FNR}’ /etc/fstab /etc/inittab
FILENAME :当前文件名
    awk ‘{print FILENAME}’ /etc/fstab
ARGC :命令行参数的个数
    awk ‘{print ARGC}’ /etc/fstab /etc/inittab
    awk ‘BEGIN {print ARGC}’ /etc/fstab /etc/inittab
ARGV :数组,保存的是命令行所给定的各参数
      awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab
      awk ‘BEGIN {print ARGV[1]}’ /etc/fstab /etc/inittab

printf命令:
格式化输出:printf  “FORMAT ”, item1, item2, …
(1)  必须指定FORMAT
(2)  不会自动换行,需要显式给出换行控制符,\n
(3)  FORMAT 中需要分别为后面每个item 指定格式符
     格式符:与item 一一对应
%c:  显示字符的ASCII码 码
%d, %i:  显示十进制整数
%e, %E: 显示科学计数法数值
%f :显示为浮点数
%g, %G :以科学计数法或浮点形式显示数值
%s :显示字符串
%u :无符号整数
%%:  显示% 自身
  修饰符:

[.#] :第一个数字控制显示的宽度;第二个# 表示小数点后精度,%3.1f

      –   :  左对齐(默认右对齐) %-15s                                           #加上负号表示左对齐
      +   :显示数值的正负符号 %+d
exam:
? awk -F: ‘{printf “%s”,$1}’ /etc/passwd
? awk -F: ‘{printf “%s\n”,$1}’ /etc/passwd
? awk -F: ‘{printf “%-20s %10d\n”,$1,$3}’ /etc/passwd
? awk -F: ‘{printf “Username: %s\n”,$1}’ /etc/passwd
? awk -F: ‘{printf “Username: %s,UID:%d\n”,$1,$3}’ /etc/passwd
? awk -F: ‘{printf “Username: %15s,UID:%d\n”,$1,$3}’ /etc/passwd
? awk -F: ‘{printf “Username: %-15s,UID:%d\n”,$1,$3}’ /etc/passwd

算术操作符:
x+y, x-y, xy, x/y, x^y, x%y
-x:  转换为负数
+x:  转换为数值
   字符串操作符:没有符号的操作符,字符串连接
   赋值操作符:
            =, +=, -=, *=, /=, %=, ^=
            ++, —
  比较操作符:
            ==, !=, >, >=, <, <=
  模式匹配符:~ :左边是否和右边匹配包含 !~ :是否不匹配
        awk –F: ‘$0 ~ /root/{print $1}‘ /etc/passwd    #整行包括root就打印
        awk ‘$0~“^root”‘ /etc/passwd                   #行首包含root
        awk ‘$0 !~ /root/‘ /etc/passwd
        awk –F: ‘$3==0’ /etc/passwd
        awk -F: ‘$3==1000{print $1,$3}’ /etc/passwd    #一个等号是赋值,2个比较

逻辑 操作符:与&& ,或|| ,非!  
   示例:
? awk –F: ‘$3>=0 && $3<=1000 {print $1}’ /etc/passwd
? awk -F: ‘$3==0 || $3>=1000 {print $1}’ /etc/passwd
? awk -F: ‘!($3==0) {print $1}’ /etc/passwd
? awk -F: ‘!($3>=500) {print $3}’ /etc/passwd      #取反需要小括号
  函数调用: function_name(argu1, argu2, …)
  条件表达式(三目表达式):
selector?if-true-expression:if-false-expression
      #为真就执行第一个命名,为假执行第二个命令
? 示例:
      awk -F: ‘{$3>=1000?usertype=”Common User”:usertype=”Sysadmin”;printf 
               “%15s:%-s\n”,$1,usertype}’ /etc/passwd

PATTERN: 根据pattern 条件,过滤匹配的行,再做处理
(1) 如果未指定:空模式,匹配每一行
(2) /regular expression/ :仅处理能够模式匹配到的行,需要用/ /括起来
      awk ‘/^UUID/{print $1}’ /etc/fstab
      awk ‘!/^UUID/{print $1}’ /etc/fstab
(3) relational expression:  关系表达式,结果为“真”才会被处理
    真:结果为非0 值,非空字符串          #“0”表示有字符串0,为真
    假:结果为空字符串或0值               #0为假 ,0以外的都是真
    示例:
? awk -F: ‘i=1;j=1{print i,j}’ /etc/passwd   #i=1;分号单独打印了,用,就代表一起
? awk ‘!0’ /etc/passwd ; awk ‘!1’ /etc/passwd  
? awk –F: ‘$3>=1000{print $1,$3}’ /etc/passwd
? awk -F: ‘$3<1000{print $1,$3}’ /etc/passwd
? awk -F: ‘$NF==”/bin/bash”{print $1,$NF}’ /etc/passwd
? awk -F: ‘$NF ~ /bash$/{print $1,$NF}’ /etc/passw
(4) line ranges   : 行范围   也支持贪婪模式
    startline,endline :/pat1/,/pat2/  不支持直接给出数字格式
    awk -F: ‘/^root\>/,/^nobody\>/{print $1}’  /etc/passwd
    awk -F: ‘(NR>=10&&NR<=20){print NR,$1}’    /etc/passwd
(5) BEGIN/END 模式
      BEGIN{}:  仅在开始处理文件中的文本之前执行一次
      END{} :仅在文本处理完成之后执行一次
      seq 10|awk ‘i=!i’ :当i=1的时候打印第一行,循环到第二行时候1取反等于0
                          不打印第二行,第三行0取反为真,打印第三行,打印奇数行
      seq 10 |sed -n ‘1~2p’

awk控制语句:
 { statements;… }  组合语句
 if(condition) {statements;…}
 if(condition) {statements;…} else {statements;…}
 while(conditon) {statments;…}
 do {statements;…} while(condition)
 for(expr1;expr2;expr3) {statements;…}
 break
 continue
 delete array[index]
 delete array
 exit

语法:if(condition){statement;…}[else statement]
      if(condition1){statement1}else if(condition2){statement2}
      else{statement3}                #条件和语句一起使用,条件用()
    使用场景:对awk 取得的整行或某个字段做条件判断
    完整的语句之间使用不要用使用;号   {}中使用;号
    示例:
awk -F: ‘{if($3>=1000)print $1,$3}’ /etc/passwd
awk -F: ‘{if($NF==”/bin/bash”) print $1}’ /etc/passwd
awk ‘{if(NF>5) print $0}’ /etc/fstab
awk -F: ‘{if($3>=1000) {printf “Common user: %s\n”,$1} else
         {printf “root or Sysuser: %s\n”,$1}}’ /etc/passwd
awk -F: ‘{if($3>=1000) printf “Common user: %s\n”,$1;
         else printf “root or Sysuser: %s\n”,$1}’ /etc/passwd
df -h|awk -F% ‘/^\/dev/{print $1}’|awk ‘$NF>=80{print $1,$5}‘
awk ‘BEGIN{ test=100;if(test>90){print “very good”}
        else if(test>60){ print “good”}else{print “no pass”}}’

while 循环
  语法:while(condition){statement;…}
  条件“真”,进入循环;条件“假”,退出循环
  使用场景:
        对一行内的多个字段逐一类似处理时使用
        对数组中的各元素逐一处理时使用
  示例:
      awk ‘/^[[:space:]]
linux16/{i=1;while(i<=NF)       #NF字段数
            {print $i,length($i); i++}}’ /etc/grub2.cfg
      awk ‘/^[[:space:]]linux16/{i=1;while(i<=NF) {if(length($i)>=10)
            {print $i,length($i)}; i++}}’ /etc/grub2.cfg
  length:统计有几个字符,awk自带的函数‘
      akw ‘BEGIN{print length(“aaaaaaaaa”)}’  #统计有几个a

do-while 循环
  语法:do {statement;…}while(condition)
  意义:无论真假,至少执行一次循环体                 #意思是先执行再循环
  示例:
? awk ‘BEGIN{ total=0;i=0;do{
      total+=i;i++;}while(i<=100);print total}‘
  思考:下面两语句有何不同?
? awk ‘BEGIN{i=0;print ++i,i}’          #先赋值,再打印
? awk ‘BEGIN{i=0;print i++,i}’          #先打印i 再赋值

 for 循环
  语法:for(expr1;expr2;expr3) {statement;…}
  常见用法:
for(variable assignment;condition;iteration process){for-body}
  特殊用法:能够遍历数组中的元素;
语法:for(var in array) {for-body}
  示例:
   awk ‘/^[[:space:]]
linux16/{for(i=1;i<=NF;i++) {print$i,length($i)}}’ 
                                                              /etc/grub2.cfg

1到100相加的几种方法:awk效率最高
 time (awk ‘BEGIN{
              total=0;for(i=0;i<=10000;i++){total+=i;};print total;}’)
 time(total=0;for i in {1..10000};do total=$(($total+i));done;echo $total)
 time(for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
 time(seq –s ”+” 10000|bc)           #-s指定分隔符,默认是换行

 switch 语句
  语法:switch(expression) {case VALUE1 or /REGEXP/:
      statement1; case VALUE2 or /REGEXP2/: statement2; …; default: statementn}
   break 和continue
? awk ‘BEGIN{sum=0;for(i=1;i<=100;i++)  {if(i%2==0)continue;sum+=i}print sum}‘
? awk ‘BEGIN{sum=0;for(i=1;i<=100;i++)  {if(i==66)break;sum+=i}print sum}‘
   break [n]
 continue [n]
 next:  提前结束对本行处理而直接进入下一行处理(awk 自身循环)
          awk -F: ‘{if($3%2!=0) next; print $1,$3}’ /etc/passwd

关联数组:array[index-expression]
          index-expression:
? (1) 可使用任意字符串;字符串要使用双引号括起来
? (2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,
      并将其值初始化为“空串”
?  若要判断数组中是否存在某元素,要使用“index in array”格 式进行遍历
   示例:
? weekdays[“mon”]=”Monday“
? awk ‘BEGIN{weekdays[“mon”]=”Monday”; 
       weekdays[“tue”]=”Tuesday”;print weekdays[“mon”]}‘
? awk ‘!arr[$0]++’ dupfile         #出现重复的$0 加1但是不再打印  和uniq -c 类似
? awk ‘{!arr[$0]++;print $0, arr[$0]}’ dupfile
  若要遍历数组中的每个元素,要使用for 循环
   for(var in array) {for-body}
   注意:var 会遍历array 的每个索引
   示例:
? awk ‘BEGIN{weekdays[“mon”]=”Monday”;weekdays[“tue”]=”Tuesday”;for(i in 
        weekdays) {print weekdays[i]}}‘
? netstat -tan | awk ‘/^tcp/{state[$NF]++}END {for(i in state)
         { print i,state[i]}}’
? awk ‘{ip[$1]++}END{for(i in ip) {print i,ip[i]}}’  /var/log/httpd/access_log

数值处理:
      rand() :返回0 和1 之间一个随机数
          awk ‘BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }’
字符串处理:
? length([s])  : 返回指定字符串的长度
? sub(r,s,[t]) : 对t 字符串进行搜索r 表示的模式匹配的内容,
                  并将第一个匹配的内容替换为s
          echo “2008:08:08 08:08:08” | awk ‘sub(/:/,“-“,$1)’
? gsub(r,s,[t]) :对t 字符串进行搜索r 表示的模式匹配的内容,并全部替换
                为s 所表示的内容
          echo “2008:08:08 08:08:08” | awk ‘gsub(/:/,“-“,$0)’
? split(s,array,[r]) :以r 为分隔符 ,切割字符串s ,并将切割后的结果保存
  至array所表示的 数组中,第一个索引值为1, 第二个索引值为2,…
          netstat -tan | awk ‘/^tcp\>/{split($5,ip,”:”);count[ip[1]]++}
          END{for (i in count) {print i,count[i]}}’

自定义函数
  格式:
function name ( parameter, parameter, … ) {
            statements
            return expression
}
  示例:

cat fun.awk

function max(v1,v2) {                  #函数名和参数(形参)
      v1>v2?var=v1:var=v2              #三目表达式
      return var                       #函数的值就是var
}
BEGIN{a=3;b=2;print max(a,b)}          #实参

awk –f fun.awk                        #-f 调用awk函数

system 命令               #awk中可以调用shell命令
  空格是awk 中的字符串连接符,如果system 中需要使用awk中
    的变量可以使用空格分隔,或者说除了awk 的变量外其他一律用”” 引用起来。
           awk BEGIN‘{system(“hostname”) }’
           awk ‘BEGIN{score=100; system(“echo your score is ” score) }’

将awk 程序写成脚本,直接调用或执行
  示例:

cat f1.awk

    if($3>=1000)print $1,$3}

awk -F: -f f1.awk /etc/passwd

 

cat f2.awk

    #!/bin/awk –f
    #this is a awk script
        {if($3>=1000)print $1,$3}

chmod +x f2.awk

f2.awk –F: /etc/passwd

格式:
  awkfile var=value var2=value2… Inputfile      #在脚本外面传递参数        
  注意 :在BEGIN 过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v参数,
  让awk 在执行BEGIN 之前得到变量的值。命令行中每一个指定的变量都需要一个-v 参数
   示例:

cat test.awk

    #!/bin/awk –f
    {if($3 >=min && $3<=max)print $1,$3}
        #chmod +x test.awk
        #test.awk -F: min=100 max=200 /etc/passw    
      这里的赋值可以再print里面使用,再BEGIN和END里面需要-v min=200
      但是建议习惯使用-v不管如何都能使用,免得遗忘

AWK运用