美文网首页
描述awk命令和函数的用法及示例

描述awk命令和函数的用法及示例

作者: 华龙007 | 来源:发表于2018-05-26 16:48 被阅读0次

    一、简介

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

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

    二、命令的用法

    命令格式

    gawk [options] 'program...' FILE ...

    常见选项

    -F fs:指明字段分隔符;
    -v var=value:自定义变量;

    program语句

    常见格式:PATTERN{ACTION STATEMENTS} #语句之间用分号分隔

    1. print
    • 逗号分隔符
    • 输出的各item可以是字符串、数值、记录的字段、变量或awk表达式
    • 省略item,打印全部字段,相当于print $0。
    1. 变量
    • 内建变量
      FS:输入字段分隔符,默认为空白字符;
      OFS:输出字段分隔符,默认为空白字符;
      RS:输入时的换行符;
      ORS:输出时的换行符;
      NF:每行的字段数,$NF表示最后一个字段;
      NR:表示行数;
      FNR:若提供多个文件,则分别计算各文件的行数;
      FILENAME:当前文件名;
      ARGC:命令行参数的个数;
      ARGV:参数数组,保存的是命令行给定的各参数;
    • 自定义变量
      (1) -v var=value #变量名区分字符大小写;
      (2) 在program中直接定义
    1. printf命令
    • 格式化输出:printf FORMAT, item1, item2, ...
      (1) FORMAT必须给出;
      (2) 不会自动换行,需要显式给出换行控制符,\n
      (3) FORMAT中需要分别为后面的每个item指定一个格式化符号;
    • 格式符:
      %c: 显示字符的ASCII码;
      %d, %i: 显示十进制整数;
      %e, %E: 科学计数法数值显示;
      %f:显示为浮点数;
      %g, %G:以科学计数法或浮点形式显示数值;
      %s:显示字符串;
      %u:无符号整数;
      %%: 显示%自身;
    • 修饰符:
      #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度。 %3.1f
      -: 左对齐
      +:显示数值的符号
    1. 操作符
      算术操作符: x+y, x-y, x*y, x/y, x^y, x%y,-x,+x
      字符串操作符:没有符号的操作符,字符串连接
      赋值操作符:=, +=, -=, *=, /=, %=, ^=, ++, --
      比较操作符:>, >=, <, <=, !=, ==
      模式匹配符::是否匹配,!:是否不匹配
      逻辑操作符:&&,||,!
      函数调用:function_name(argu1, argu2, ...)
      条件表达式:selector?if-true-expression:if-false-expression

    2. PATTERN
      (1) empty:空模式,匹配每一行;
      (2) /regular expression/:仅处理能够被此处的模式匹配到的行;
      (3) relational expression: 结果为真才会被处理;真为非0值,非空字符串;
      (4) line ranges:行范围,startline,endline:/pat1/,/pat2/
      注意: 不支持直接给出数字的格式
      ~]# awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd
      (5) BEGIN/END模式
      BEGIN{}: 仅在开始处理文件中的文本之前执行一次;
      END{}:仅在文本处理完成之后执行一次;

    3. 常用的action
      (1) Expressions
      (2) Control statements:if, while等;
      (3) Compound statements:组合语句;
      (4) input statements
      (5) output statements

    4. 控制语句

    • if(condition) {statments}
      if(condition) {statments} else {statements}
      while(conditon) {statments}
      do {statements} while(condition)
      for(expr1;expr2;expr3) {statements}
      break
      continue
      delete array[index]
      delete array
      exit
      { statements }

    • if-else:if(condition) statement [else statement]
      ~]# awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
      ~]# awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
      ~]# awk '{if(NF>5) print $0}' /etc/fstab
      ~]# df -h | awk -F[%] '/^/dev/{print $1}' | awk '{if($NF>=20) print $1}'
      使用场景:对awk取得的整行或某个字段做条件判断;

    • while:while(condition) statement
      条件“真”,进入循环;条件“假”,退出循环;
      ~]# awk '/^[[:space:]]linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
      ~]# awk '/^[[:space:]]
      linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg
      使用场景:对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用;

    • do-while:do statement while(condition)
      意义:至少执行一次循环体

    • for:for(expr1;expr2;expr3) statement
      for(variable assignment;condition;iteration process) {for-body}
      ~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg

      特殊用法:
      能够遍历数组中的元素;
      语法:for(var in array) {for-body}

    • switch
      switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}

    • break和continue
      break [n]
      continue

    • next
      提前结束对本行的处理而直接进入下一行;
      ~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd

    • array

    关联数组:array[index-expression]
    index-expression:
    (1) 可使用任意字符串;字符串要使用双引号;
    (2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”;
    若要判断数组中是否存在某元素,要使用"index in array"格式进行;
    weekdays[mon]="Monday"
    若要遍历数组中的每个元素,要使用for循环;
    for(var in array) {for-body}
    ~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
    注意:var会遍历array的每个索引;
    如果数组不存在,我们引用数组,会创建,值为空,引用数组的值为0。

    • 函数

    1.内置函数
    数值处理:
    rand():返回0和1之间一个随机数
    字符串处理:
    length([s]):返回指定字符串的长度;
    sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容;t中有没有r,有替换成s
    gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容;
    split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中;
    ~]# netstat -tan | awk '/^tcp>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'

    2.自定义函数

    三、入门实例

    1.把文本分片后排列整齐,并加上标签
    [root@localhost sctript]# cat awk01 
     Mike Harrington:(510) 548-1278:250:100:175
     Christian Dobbins:(408) 538-2358:155:90:201
     Susan Dalsass:(206) 654-6279:250:60:50
     Archie McNichol:(206) 548-1348:250:100:175
     Jody Savage:(206) 548-1278:15:188:150
     Guy Quigley:(916) 343-6410:250:100:175
     Dan Savage:(406) 298-7744:450:300:275
     Nancy McNeil:(206) 548-1278:250:80:75
     John Goldenrod:(916) 348-4278:250:100:175
     Chet Main:(510) 548-5258:50:95:135
     Tom Savage:(408) 926-3456:250:168:200
     Elizabeth Stachelin:(916) 440-1763:175:75:300
    
    [root@localhost sctript]# awk -F: 'BEGIN{printf "%-20s %-15s %-4s %-4s %-4s\n","Name","PHone","Jan","Feb","MAR"}BEGIN{print"--------------------------------------------------"}{printf "%-20s %-15s %-4s %-4s %-4s\n",$1,$2,$3,$4,$5}' awk01
    
    Name                 PHone           Jan  Feb  MAR 
    --------------------------------------------------
     Mike Harrington     (510) 548-1278  250  100  175 
     Christian Dobbins   (408) 538-2358  155  90   201 
     Susan Dalsass       (206) 654-6279  250  60   50  
     Archie McNichol     (206) 548-1348  250  100  175 
     Jody Savage         (206) 548-1278  15   188  150 
     Guy Quigley         (916) 343-6410  250  100  175 
     Dan Savage          (406) 298-7744  450  300  275 
     Nancy McNeil        (206) 548-1278  250  80   75  
     John Goldenrod      (916) 348-4278  250  100  175 
     Chet Main           (510) 548-5258  50   95   135 
     Tom Savage          (408) 926-3456  250  168  200 
     Elizabeth Stachelin (916) 440-1763  175  75   300 
    
    2.删除行内与第一列字符相同的字符
    [root@localhost sctript]# cat c
    a b c a d a
    s d d d x s a
    h j s a s h j h
    j d f j a s j k j
    
    [root@localhost sctript]# awk '{a=$1;gsub(" ?"a,"");print a""$0}' c
    
    a b c d
    s d d d x a
    h j s a s j
    j d f a s k
    

    a=$1把第一列赋值变量a。
    gsub(" ?"a,"")用函数gsub进行行内全局替换," ?"a是正则,表示前面的空格可以没有也可以有一次,把匹配到的全部替换成空格。
    print a""$0打印,在行首添加上变量a,也就字段$1。

    3.打印学生的平均成绩
    [root@localhost sctript]# cat d
    Sophia huaxue 90
    Faye   huaxue 80
    Sophia wuli   70
    Faye   wuli   60
    
    [root@localhost sctript]# awk '{b[$1]=$1} {a[$1]++;c[$1]+=$3} END {for(i in b);for(i in a);for(i in c){y=c[i]/a[i];printf("%-8s平均成绩为:\t%2.2f\n",b[i],y)}}' d
    
    Sophia  平均成绩为:  80.00
    Faye    平均成绩为:  70.00
    

    分析分两步:
    1.先打印名字
    awk '{b[$1]=$1}END{for(i in b)print b[i]}' d
    Sophia
    Faye
    {b[$1]=$1}以名字为变量b的下标,并在里面赋值名字。
    END{for(i in b)print b[i]}在最后,遍历数组b,并打印每一个元素。

    2.打印平均分数
    ]# awk '{a[$1]++;c[$1]+=$3}END{for(i in a){y=c[i]/a[i];print y}}' d
    80
    70
    {a[$1]++;c[$1]+=$3}以名字为变量a的下标,自增计数。以名字为变量c的下标,拿自己和$3分数的相加在赋值给自己。
    {y=c[i]/a[i];print y}总分除以计数的到平均分

    把两个整合awk '{b[$1]=$1} {a[$1]++;c[$1]+=$3} END {for(i in b);for(i in a);for(i in c){y=c[i]/a[i];printf("%-8s平均成绩为:\t%2.2f\n",b[i],y)}}' d

    4.文本a记录姓名、学号,文本b记录学号、学科、成绩。把a和b组合成一张表格。
    [root@localhost sctript]# cat a
    Sophia|0001
    Faye|0002
    
    [root@localhost sctript]# cat b
    0001|Chinese|77
    0001|Music|90
    0002|Chinese|62
    0002|Music|80
    
    [root@localhost sctript]# awk -F \| 'NR==FNR{a[$2]=$1;next}{printf "%-8s %-6s %-8s %-2s\n",a[$1],$1,$2,$3}' a b
    
    Sophia   0001   Chinese  77
    Sophia   0001   Music    90
    Faye     0002   Chinese  62
    Faye     0002   Music    80
    

    NR表示a数据和b数据的总行数,FNR表示a和b分别计数
    NR==FNR为真时表示读入的是a数据,执行之后的代码a[$2]=$1;next
    a[$2]=$1;next表示把a文件切片的字段1放入数组a,数组a下标用字段2的值
    NR==FNR为假时表示读入的是b数据,执行printf中的代码

    printf "%-8s %-6s %-8s %-2s\n",a[$1],$1,$2,$3格式化输出b文件切片后的值
    a[$1],$1,$2,$3,其中第一个a[$1]是读取a文件时新建的数组a,这是个关联数组,文件a中$2的值等于文件b中$1的值,所以就可以把文件a存入数组的值和文件b的各字段数值一起打印出来。

    5.把文本分片后按字母顺序输出相同两个字符的字段
    [root@localhost sctript]# cat sting 
    asf|zz|123|bb|q23|cc|py|dd|ab|ee|x6|ff|ffq|gg|aaa|hh|aa
    
    [root@localhost sctript]# awk -F \| '{for(i=1;i<=NF;i++){if($i~/^([a-z]){2}$/) {a=substr($i,1,1);b=substr($i,2,2);if(a==b) print $i | "sort"}}}' sting 
    
    aa
    bb
    cc
    dd
    ee
    ff
    gg
    hh
    zz
    

    文本切割后按照字段个数挨个循环,用模式匹配出只有2个字母的字段,对这个字段的两个字母对比,相等时打印并传递到sort命令中排序。

    6.把grades中每行的字段分别从小到大排序
    [root@localhost sctript]# cat grades 
    44 55 66 22 77 99
    100 22 77 99 33 66
    55 66 100 99 88 45
    
    [root@localhost sctript]# vim sorter.awk 
    #!/bin/awk -f
    #这里我用选择排序算法进行排序
    {for(i=0;i<NF;i++){array[i]=$(i+1)}}
    sort(array)
    
    function sort (temp){
            for(i=0;i<length(temp)-1;i++){
                    min=i
                    for(j=i+1;j<length(temp);j++){
                            if(temp[min]>temp[j]){
                                    min=j
                            }
                    }
                    if(min!=i){
                            temp1=temp[i]
                            temp[i]=temp[min]
                            temp[min]=temp1
                    }
            }
            for(i=0;i<NF;i++){
                    printf("%d ", temp[i])
            }
            printf "\n"
    }
    
    [root@localhost sctript]# ./sorter.awk grades
    22 44 55 66 77 99 
    22 33 66 77 99 100 
    45 55 66 88 99 100
    

    相关文章

      网友评论

          本文标题:描述awk命令和函数的用法及示例

          本文链接:https://www.haomeiwen.com/subject/abvsjftx.html