awk

作者: 六月天的安静 | 来源:发表于2017-07-17 22:22 被阅读51次

    awk的简介和功能

    • awk: Linux 文本处理三剑客:grep、sed和awk。其中grep是一种文本过滤工具,sed是文本行编辑器,而 awk 是一种报表生成器,就是对文件进行格式化处理的,这里的格式化不是文件系统的格式化,而是对文件内容进行各种“排版”,进而格式化显示。

    • 在 linux 之上我们使用的是 GNU awk 简称 gawk ,并且 gawk 其实就是 awk 的链接文件,因此在系统上使用 awk和 gawk是一样的。我们通过 man gawk 的相关功能说明——gawk - pattern scanning and processing language (模式扫描及处理语言),gawk 是一种过程式编程语言。gawk 还支持条件判断、数组、循环等各种编程语言中 所有可以使用的功能,因此我们还可以把 gawk 称为一种脚本语言解释器

    • awk在处理文本时也是一次读取一行文本,然后根据输入分隔符(默认为空格符)进行切片,切成 n 个片段,然后将每一片都赋予 awk 内部的一个变量当中来进行保存,这些变量名为 $1、$2、$3......一直到最后一个,awk 就可以对这些片段单独处理,比如显示某一段、特定段,甚至可以对某些片段进行额外的加工处理,比如计数、运算等。

    awk命令格式和选项

    语法形式 :

    • awk [OPPTIONS] 'program' FILE1 FILE2 FILE3...
      其中 program :PARTTERN{action statement}

    • action statement : 动作语句,可以有多个语句组成,各语句间使用分号分割 ;

    • OPPTIONS :
      -F fs :fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:
      -v var=value : 赋值一个用户定义变量,将外部变量传递给awk
      -f scripfile : 从脚本文件中读取awk命令

    输出命令print和printf

    (1)print

    • 用法:print item1,item2,......
      • item : 字符串,用引号引用;print " readhat" , "linux"
      • 变量:显示变量的值 ,可以直接使用变量的名进行引用。print var_name
      • 数值:无需加引号
    • 要点:
      • 各 item 之间使用逗号分开;而输出时的分隔符为默认空白字符
      • 输出的各item可以为字符串或数值、当前记录的字段($#)、变量或awk的表达式;数值会被隐式转换为字符串进行输出
      • print后面的item省略时,相当于运行“$0”,用于输出整行;
      • 输出空白字符:print " "

    (2)printf
    语法:printf FORMAT item,item2,......
    要点:

    • 必须提供FORMAT
    • 与print语句不同,printf不会自动换行,需要显示指定换行符:\n
    • FORMAT中需要分别为后面的每个item指定一个格式符,否则 item 无法显示:格式符都以%开头,后跟单个字符:
    格式 描述
    %d ,%i 十进制有符号整数
    %u 十进制无符号整数
    %f 浮点数
    %s 字符串
    %c 单个字符
    %e,%E 科学计数法显示数值
    %x 以十六进制表示的整数
    %o 以八进制表示的整数

    awk 'BEGIN{n1=11.234;n2=12;n3=97;n4=16;printf("%.2f,%.3f,%x,%o\n",n1,n2,n3,n4);}

    变量

    (1)awk内置变量:

        $n  :当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。
        $0  :这个变量包含执行过程中当前行的文本内容。
        FS  :输入时字段分隔符(默认是任何空格)。
        RS  :记录分隔符(默认是一个换行符)。
        OFS :输出时字段分隔符(默认值是一个空格)。
        ORS :输出记录分隔符(默认值是一个换行符)
        NF  :表示字段数,在执行过程中对应于当前的字段数。
        NR 表示记录数,命令后跟的所有文件将统一合并基数
        FNR :行数,各文件单独计数
        FILENAME :当前正被awk读取的文件的文件名
        ARGC :awk命令行中的参数的个数
        ARGV :包含命令行参数的数组
    

    例如:

    1、打印 /etc/passwd 的第一列(用户名)和第三列(id号)信息
    (1)用F指定分割符
    awk -F: '{print $1,$3}' /etc/passwd
    (2)输出时中间加个TAB键
    awk -F: '{print $1,"\t",$3}' /etc/passwd

         (3)用内置变量指定分隔符
             `awk -v FS=: '{print $1,$3}' /etc/passwd`
         (4)用内置变量指定分隔符,并且在打印时再次引用此变量用于输出时字段之间的分隔符
            ` awk -v FS=: '{print $1,FS,$3}' /etc/passwd`
         (5)定义变量s,并且把此变量赋予内置变量FS
            ` s=:;awk -v FS=$s '{print $1,FS,$3}' /etc/passwd`
         (6)输出时给每个字段前面加上描述信息
             `awk -F: '{print "user: "$1,"id:"$3}' /etc/passwd`
         (7)指定输出时的分割符为“---”
             `awk -F: -v OFS='---' '{print $1,$3}' /etc/passwd    `  
    

    2、读取每个分区的使用率
    df | awk '/^\/dev\/sd/{print $5}'
    df|grep "^/dev/sd"|awk '{print $5}'|awk -F% '{print $1}'

    3、取 /etc/fstab 文件中的挂载点
    cat /etc/fstab |grep "^UUID"|awk '{print $2}'

    4、统计一下/etc/passwd/文件中每个记录(行)的字段数

    (1)每行都会统计一下
    awk -F: '{print NF}' /etc/passwd
    (2)只在结束的时候统计一遍
    awk -F: 'END{print NF}' /etc/passwd

    5、统计某个文件的行数
    awk 'END{print NR}' /etc/passwd
    awk 'BEGIN{while(getline < "/etc/passwd" > 0) { i++ }; print i }'

    6、统计/etc/passwd和/etc/fstab的行数,两文件合并统计
    awk '{print NR,$0}' /etc/passwd /etc/fstab
    awk 'END{print NR}' /etc/passwd /etc/fstab

    9、统计/etc/passwd和/etc/fstab的行数,两文件单独统计
    awk '{print FNR,$0}' /etc/passwd /etc/fstab

    7、统计命令中的参数:
    awk 'END{print ARGC}' /etc/passwd /etc/fstab

    8、查看此命令行中的参数:
    (1)查看此命令行中的第一个参数
    awk 'END{print ARGV[0]}' /etc/passwd /etc/passwd
    (2)查看此命令行中的第二个参数
    awk 'END{print ARGV[1]}' /etc/passwd /etc/passwd
    (3)查看此命令行中的第二个参数
    awk 'END{print ARGV[2]}' /etc/passwd /etc/passwd

    9、使用print $NF可以打印出一行中的最后一个字段,使用$(NF-1)则是打印倒数第二个字段,其他以此类推:
    echo -e "line1 f2 f3\nline2 f4 f5"|awk '{print $NF}'
    echo -e "line1 f2 f3\nline2 f4 f5"|awk '{print $(NF-1)}'

    10、计算5个数相加:
    seq 5|awk 'BEGIN{sum=0;print "相加"}{print $1"+";sum+=$1}END{print "等于";print sum}'

    (2)自定义变量

    将外部变量值传递给var

    1、借助 -v 选项,可以将外部值(并非来自stdin)传递给awk:
    name=aaa;echo|awk -v var=$name 'END{print var}'

    另一种传递外部变量方法:

    var1=aaa
    var2=bbb
    echo | awk '{print v1,v2 }' v1=$var1 v2=$var

    当输入来自于文件时使用:

    awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename

    awk的运算与判断

    作为一种程序设计语言所应具有的特点之一,awk支持多种运算,这些运算与C语言提供的基本相同。awk还提供了一系列内置的运算函数(如log、sqr、cos、sin等)和一些用于对字符串进行操作(运算)的函数(如length、substr等等)。这些函数的引用大大的提高了awk的运算功能。作为对条件转移指令的一部分,关系判断是每种程序设计语言都具备的功能,awk也不例外,awk中允许进行多种测试,作为样式匹配,还提供了模式匹配表达式 ~(匹配)~!(不匹配)。作为对测试的一种扩充,awk也支持用逻辑运算符。

    Paste_Image.png

    例:

    awk 'BEGIN{a="2";print a++,++a}'

    赋值运算符

    逻辑运算符

    例:

    awk 'BEGIN{a=1;b=2;print(a>5 && b<=2),(a>5 || b<=2)}'

    正则运算符

    awk 'BEGIN{a="wangtest";if(a~/^wang*/){print "ok"}}'

    关系运算符


    例:

    awk 'BEGIN{a=11;if(a>=9){print "ok"}}'

    其它运算符


    例子:

    1、三目运算符(?):
    awk 'BEGIN{a=1;b=2;{print a==b ? "ok" : "err"}}'
    2、遍历数组
    awk 'BEGIN{arr[1]="first";arr[2]="second";for(i in arr){print arr[i]}}'

    流程控制语句

    在 linux awk 的 while、do-while 和 for 语句中允许使用 break , continue 语句来控制流程走向,也允许使用exit这样的语句来退出。break中断当前正在执行的循环并跳到循环外执行下一条语句。if 是流程选择用法。awk中,流程控制语句,语法结构,与c语言类型。有了这些语句,其实很多shell程序都可以交给awk,而且性能是非常快的。下面是各个语句用法。

    条件判断语句

    if 语句:

    if(表达式)
      语句1
    else
    语句2
    

    格式中语句1可以是多个语句,为了方便判断和阅读,最好将多个语句用{}括起来。awk分枝结构允许嵌套,其格式为:

    if(表达式)
      {语句1}
    else if(表达式)
      {语句2}
    else
     {语句3}
    

    示例:

    awk 'BEGIN{
    test=65;
     if(test>90){
        print "优秀";
        } 
        else if(test>=60){
          print "良好";
        } 
      else{
        print "不及格";
       } 
     }' 
    

    循环语句:

    (1)while 语句

    while(表达式)
        {语句}
    

    示例:

    awk 'BEGIN{
    i=1;
    test=100;
    total=0;
    while(i<=100){
       total+=i;
       i++;
     }
       print total;
     }'
    

    (2)for 循环

    for 循环有两种格式:
    格式一:

    for(变量 in 数组)
     {语句}
    

    例子:

    显示Socket所处不同样状态个数
    ss -ant|awk '!/^S/{state[$1]++} END{for(i in state){print i,state[i]}}'

    格式二:

    for(变量;条件;表达式)
     {语句}
    

    例子:

    awk 'BEGIN{
    total=0;
    for(i=0;i<=100;i++){
    total+=i;
    }
    print total;
    }'
    
    结果:5050
    

    do循环

    do
    {语句} while(条件)
    

    例子:

    awk 'BEGIN{
    i=1;
    total=0;
    do{total+=i;i++;}while(i<=100)
    print total;
    }'
    
    结果:5050
    

    其它语句:

    break: 当 break 语句用于 while 或 for 语句时,导致退出程序循环。
    continue: 当 continue 语句用于 while 或 for 语句时,使程序循环移动到下一个迭代。
    next: next 能够导致读入下一个输入行,并返回到脚本的顶部。这可以避免对当前输入行执行其他的操作过程。
    exit:exit 语句使主输入循环退出并将控制转移到END,如果END存在的话。如果没有定义END规则,或在END中应用exit语句,则终止脚本的执行。

    数组的定义

    数字做数组索引(下标):

    Array[1]="sun"
    Array[2]="kai"
    

    字符串做数组索引(下标):

    Array["first"]="www"
    Array["last"]="name"
    Array["birth"]="1994"
    

    读取数组的值:

    { for(item in array) {print array[item]}; }
    { for(i=1;i<=len;i++) {print array[i]}; }
    

    数组相关函数

    • 例:得到数组的长度:

    awk 'BEGIN{info="it is a test";lens=split(info,array," ");print length(array),lens}'
    结果:4 4
    length返回字符串以及数组长度,split进行分割字符串为数组,也会返回分割得到数组长度。

    • asort对数组进行排序,返回数组长度。

    awk 'BEGIN{info="it is a test";split(info,array," ");print asort(array)}'

    • 输出数组内容(无序,有序输出):
    • (1)无序输出:

    awk 'BEGIN{info="it is a test";split(info,array," ");for(i in array){print i,array[i]}}'
    结果:
    4 test
    1 it
    2 is
    3 a

    • (2)有序输出:

    awk 'BEGIN{info="it is a test";lent=split(info,array," ");for(i=1;i<=lent;i++){print i,array[i]}}'
    结果:
    1 it
    2 is
    3 a
    4 test
    注意:数组下标是从1开始,与C数组不一样。

    判断键值存在以及删除键值:

    错误的判断方法:
    awk 'BEGIN{arr["a"]="a1";arr["b"]="b1";if(arr["c"]!="1"){print "no found";};for(i in arr){print i,":"arr[i];}}'
    结果:
    no found
    a a1
    b b1
    c
    以上出现奇怪问题,arr[“c”]没有定义,但是循环时候,发现已经存在该键值,它的值为空,这里需要注意,awk数组是关联数组,只要通过数组引用它的key,就会自动创建改序列。

    正确的判断方法:
    awk 'BEGIN{arr["a"]="a1";arr["b"]="b1";if("c" in arr){print "OK"};for(i in arr){print i,":",arr[i]}}'
    结果:
    a : a1
    b : b1
    注意:if(key in array)通过这种方法判断数组中是否包含 key 键

    删除键值:

    awk 'BEGIN{arr["a"]="a1";arr["b"]="b1";delete arr["a"];for(i in arr){print i,":",arr[i]}}'
    结果:
    b : b1
    注意:delete arr[key] 可以删除对应数组 key 的序列值。

    二维、多维数组的使用

    awk的多维数组在本质上实际上是一维数组,更确切一点,awk在存储上并不支持多维数组,awk提供了逻辑上模拟二维数组的访问方法。例如,array[2,4]=1这样的访问时允许的,awk使用一个特殊的字符串SUBSEP (\034)作为分割字段,在上面的例子中,关联数组array存储的键值实际上是2\034
    类似一维数组的成员测试,多维数组可以使用if( (i,j) in array)这样,但是下表必须放在圆括号中。类似一维数组的循环访问,多维数组使用for(item in array)这样的语法遍历数组,与一维数组不同的是,多维数组必须使用split()函数来访问单独的下标。

    例子:

    awk 'BEGIN{
     for(i=1;i<=9;i++){
       for(j=1;j<=9;j++){
       arr[i,j]=i*j;print i,"*",j,"=",arr[i,j];
       }
      }
    }' 
    

    结果:
    1*1 = 1
    1*2 = 2
    1*3 = 3

    1 *4 = 4
    1*5 = 5
    ..............
    9* 3 = 27
    9*4 = 36
    9*5 = 45
    9 *6 = 54
    9*7 = 63
    9*8 = 72
    9*9 = 81
    可以引用array[i,j]引用获得数组的内容

    还可以用另外一种方法:

    awk 'BEGIN{
    for(i=1;i<=9;i++){
       for(j=1;j<=9;j++){ 
         arr[i,j]=i*j; 
       } 
      } 
    for(m in arr){
      split(m,tarr,SUBSEP);print arr2[1],"*",arr2[2],"=",arr[m]; 
    } 
    }'
    

    内置函数

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

    算数函数:


    例子:

    awk 'BEGIN{OFMT="%.3f";a=sin(1);b=exp(10);c=log(10);d=int(3.1415);print a,b,c,d;}'  
    

    OFMT 设置输出数据格式是保留3位小数。

    获得随机数:

    awk 'BEGIN{srand();n=int(100*rand());print n;}'
    

    字符串函数

    格式 描述
    sub(Ere,Repl,[In]) 用Repl参数指定的字符串替换In参数指定的字符串中的由Ere参数指定的扩展正则表达式的第一个具体值。sub函数返回替换的数量。
    gsub(Ere,Repl,[in]) 用Real参数指定的字符串替换In参数指定的字符串中的由Erect参数指定的扩展正则表达式的所有值,它和sub函数函数完全一样的执行
    length(String) 返回String参数指定的字符串的长度(字符形式)。如果未给出String参数,则返回整个记录的长度($0记录变量)
    blength(String) 返回String参数指定的字符串的长度(以字节为单位)。如果未给出String参数,则返回整个记录的长度($0记录变量)
    substr(String,M,N) 返回具有N参数指定的字符数量子串。子串从String参数指定的字符串取得,其字符以M参数指定的位置开始取。M参数会将String参数中的第一个字符作为编号1。如果未指定N参数,则子串的长度将是M参数指定的位置到String参数的末尾的长度
    split(String,A,[Ere]) 以Ere参数指定的分隔符(此分隔符可以通过Ere参数指定的扩展正则表达式进行,或用当前字段分隔符(FS特殊变量)来进行)去切割字符串String,并将切割后的结果保存至A表示的数组中
    tolower(String) 返回String参数指定的字符串,字符串中每个大写字符将更改为小写
    toupper(String) 返回String参数指定的字符串,字符串中每个小写字符将更改为大写
    index(Streing1,String2) 在由String1参数指定的字符串中,返回位置,从1开始编号,如果String2参数不在String1参数出现,则返回0
    match(String,Ere) 在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)

    注:Ere 都可以是正则表达式

    gsub,sub使用:

    awk 'BEGIN{info="this is a test2017test!";gsub(/[0-9]+/,"!",info);print info}'
    结果:
    this is a test!test!

    在 info 中查找满足正则表达式:/[0-9]+/"!"替换,并且替换后的值赋值给info,未给info赋值默认是$0

    查找字符串(index使用)

    awk 'BEGIN{info="this is a test2017test!";print index(info,"test")?"包含":"不包含"}'
    结果:
    包含

    正则表达式匹配查找(match使用)

    awk 'BEGIN{info="this is a test2017test!";print match(info,/[0-9]+/)?"OK":"no found";}'
    结果:
    OK

    截取字符串(substr)使用

    法一:awk 'BEGIN{info="this is a test2017test!";print substr(info,2,12)}'
    法二:awk 'BEGIN{info="this is a test2017test!";a=substr(info,2,12);print a}'
    结果:
    his is a tes

    从第4个字符开始,截取10个长度字符串

    字符串分割(split使用)

    awk 'BEGIN{info="this is a test";split(info,arr," ");print length(arr);for(i in arr){print i,arr[i];}}'
    结果:
    4
    4 test
    1 this
    2 is
    3 a

    分割info,动态创建数组arr,这里比较有意思,awk for ...in 循环,是一个无序的循环。并不是从数组下标 1...n,因此使用时需要注意

    一般函数


    例子:

    打开外部文件(close 用法)
    awk 'BEGIN{while("cat /etc/passwd"|getline){print $0;}close("/etc/passwd")}'
    结果:
    root:x:0:0:root:/root:/bin/bash
    system:x:0:0::/home/system:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    ................................................

    逐行读取外部文件(getline使用方法)
    例1:awk 'BEGIN{while(getline < "/etc/passwd"){print $0;};close("/etc/passwd")}'
    结果:
    root:x:0:0:root:/root:/bin/bash
    system:x:0:0::/home/system:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    …………………………………………

    例2:awk 'BEGIN{print "Enter your name";getline name;print name}'
    结果:
    Enter your name
    王健
    王健

    调用外部应用程序(system使用方法)
    awk 'BEGIN{b=system("ls -al");print b;}'
    结果:
    total 102536
    dr-xr-x---. 20 root root 4096 Jul 17 10:08 .
    dr-xr-xr-x. 20 root root 267 Jul 14 22:44 ..
    -rwx--x--x. 1 root root 1916 May 18 08:55 anaconda-ks.cfg
    drwxr-xr-x. 2 root root 6 Jun 15 11:04 backup
    ...........

    时间函数

    建指定时间(mktime使用)
    awk 'BEGIN{a=mktime("2017 07 17 13 28 04");print strftime("%c",a)}'
    结果:
    Mon 17 Jul 2017 01:28:04 PM CST

    求2个时间段中间时间差
    awk 'BEGIN{a=mktime("2017 07 17 13 20 00");b=mktime("2017 07 17 13 21 00");print b-a;}'
    结果:
    60

    awk 'BEGIN{a=mktime("2017 07 17 13 48 00");b=systime();print b-a;}'
    结果:
    111

    strftime日期和时间格式说明符:

    相关文章

      网友评论

        本文标题:awk

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