美文网首页
linux命令学习(三)——awk

linux命令学习(三)——awk

作者: 陌上疏影凉 | 来源:发表于2017-05-20 23:08 被阅读184次

    学习资料:《linux大棚命令百篇上》

    awk介绍及简单实用

    以下是在网上找到的两个awk的介绍:

    • awk是一种处理文本文件的语言,是一个强大的文本分析工具。(来自http://www.runoob.com/linux/linux-comm-awk.html
    • awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势。(来自: http://man.linuxde.net/awk
    • awk的名字由来是取自创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母

    awk的命令格式和选项

    格式awk [Options] 'Pattern {Actioin}' filename
    说明

    1. Option部分,用来指定命令选项
    2. Pattern部分,用来指定判断条件,相当于我们之前说sed的行匹配语句。
    3. Action部分,用于指定awk的动作,也就是对Pattern选中行执行的操作。
    4. filename部分,用于指定awk要操作的文件名

    举个栗子

    通过以下awk命令可以将一个文件全部输出,效果同cat student

    wangsheng@ubuntu[14:44:40]:~/Documents$ awk '{print $0}' student 
    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列和第3列

    wangsheng@ubuntu[14:45:04]:~/Documents$ awk '{print $1,$3}' student 
    Marry 78
    Jack 66
    Tom 48
    Mike 87
    Bob 40
    

    awk的工作原理

    来自: http://man.linuxde.net/awk
    (以下摘抄自《linux大棚命令百篇》)

    1. awk读取一条记录(文件中一行内容)作为输入,并将这条记录赋值给内部变量$0。
    2. 记录被分符分割成多个字段,每一个字段都存储到指定编号的变量中,从$1开始。(awk内部变量FS用来指定字段的分隔符,默认情况下为空格)
    3. 对于每一条记录,按照规定的pattern进行匹配,如果匹配成功,则执行对应的action,否则不执行。pattern和action都是可选的,但两者必须提供一个。
      • 如果未指定pattern,则对所有行都执行action操作。
      • 如果未指定action,则对指定行执行{print}操作,打印匹配行的内容。
      • 如果action指定为{},则不做任何操作。

    awk的各个组成部分详解

    option

    options用来指定awk的选项,到底有哪些选项呢?我们一个一个来看看

    • -F fs fs指定输入分隔符,可以是字符串和正则表达式
    # 文件内容
    wangsheng@ubuntu[14:53:33]:~/Documents$ cat log 
    hello,world,java,linux,android
    # 不使用-F
    wangsheng@ubuntu[14:51:51]:~/Documents$ awk '{print $1,$3}' log 
    hello,world,java,linux,android 
    # 使用-F
    wangsheng@ubuntu[14:52:19]:~/Documents$ awk -F , '{print $1,$3}' log 
    hello java
    
    • -v var=value定义变量var,值为value(注意赋值语句中间不能有空格)
    # 定义一个变量并输出a+文件内容
    wangsheng@ubuntu[14:57:27]:~/Documents$ awk -v a=1 '{print a$0}' log 
    1hello,world,java,linux,android
    # 定义两个变量时,每个变量前加—v
    wangsheng@ubuntu[14:58:10]:~/Documents$ awk -v a=1000 -v b=2000 '{print a$0b}' log 
    1000hello,world,java,linux,android2000
    
    • -f scriptfile 从脚本中读取命令,当命令部分过长时,将命令部分单独放到一个脚本文件中,通过这种方式调用,跟sed里的用法是一样的,后面我们用到在举例说明。

    pattern

    说完了选项,我们来说说这个pattern,它也是可选的,表示匹配哪一行。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。pattern可以包含如下几项:

    • /正则表达式/,这个不用说了,最方便的匹配操作。
    # 打印含有Jack的行
    wangsheng@ubuntu[15:14:16]:~/Documents$ awk '/Jack/ {print}' student 
    Jack    2321 66 78 45
    
    • 关系表达式,用关系表达式的真假来判断要不要执行后面的操作。
    # NR是awk的一个内置变量,代表行号
    # 用NR%2==0这个关系表达式这个模式
    #   当结果为true时,执行{next}
    #   当结果为false时,执行{print NR,$0},将行号与该行内容输出
    wangsheng@ubuntu[15:14:50]:~/Documents$ awk 'NR%2==0 {next} {print NR,$0}' student 
    1 Marry   2143 78 84 77
    3 Tom     2122 48 77 71
    5 Bob     2415 40 57 62
    
    • 模式匹配表达式,用~表示匹配正则表达式、!~表示不匹配正则表达式。
    # 选取第1列(名字)中不含有o的那些行,打印该行内容
    wangsheng@ubuntu[15:25:35]:~/Documents$ awk '$1 !~ /o/ {print}' student 
    Marry   2143 78 84 77
    Jack    2321 66 78 45
    Mike    2537 87 97 95
    # 选取第1列中含有o的那些行,打印该行内容
    wangsheng@ubuntu[15:26:45]:~/Documents$ awk '$1 ~ /o/ {print}' student 
    Tom     2122 48 77 71
    Bob     2415 40 57 62
    
    • BEGIN{} pattern{} END{},这是awk中使用比较普遍的一种形式,BEGIN、END分别表示在开始读入第一行前和读入最后一行结束后的操作。
    • BEGIN语句块:可选的语句块,在awk开始从输入流中读取行之前被执行,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
    • pattern语句块:可选的语句块,是最重要的部分。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。
    • END语句块:可选的语句块,在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成。

    action

    常用的action当然就是print了,当然在这个action中我们能做很多事情,完全就跟用c语言编程一样,可以有条件控制、循环、赋值、数组定义使用等等,这里就不展开来说了。具体用法可以参见http://man.linuxde.net/awk。 这里要重点说一下的是awk的内置变量。

    awk的内置变量

    所谓内置变量就是指awk中自带的变量,不需要用-v来定义就可以使用的变量。常用的有以下几种:

    • $0 表示当前行的所有内容。
    • $n 表示用分割符分开后,第n个字段。
    • ARGC 表示命令行参数的数目。
    wangsheng@ubuntu[16:16:20]:~/Documents$ awk '{print ARGC}' log
    2
    
    • ARGV 表示命令行参数的数组。可以看到ARGV[0]是awk,而ARGV[1]-ARGV[n]都是文件名
    wangsheng@ubuntu[16:17:04]:~/Documents$ awk '{print ARGV[0]}' log
    awk
    wangsheng@ubuntu[16:18:35]:~/Documents$ awk '{print ARGV[1]}' log
    log
    
    • FILENAME 表示当前处理文件的文件名称。
    wangsheng@ubuntu[16:01:23]:~/Documents$ awk '{print FILENAME}' log 
    log
    wangsheng@ubuntu[21:58:04]:~/Documents$ awk '{print FILENAME}' log student 
    log
    student
    student
    student
    student
    student
    
    • FS 表示字段的分隔符,awk在输入时按FS指定的符号进行字段分割。
    wangsheng@ubuntu[16:02:05]:~/Documents$ awk -F , '{print FS}' log
    ,
    
    • OFS 输出字段分隔符(默认值是一个空格),awk输出时使用OFS指定的符号进行字段拼接。
    wangsheng@ubuntu[22:02:02]:~/Documents$ awk -F , 'BEGIN{OFS="-"} {print $1,$2,$3}' log
    hello-world-java
    
    • NR 表示已读记录数,无论有多少个文件,只要读入一条记录,该值就会加1,注意与FNR的不同之处。
    wangsheng@ubuntu[22:02:11]:~/Documents$ awk '{print NR}' log student 
    1
    2
    3
    4
    5
    6
    
    • FNR 当前文件已读的记录数,操作当前文件时,每读入一条记录,FNR就会加1,更换文件后,该变量会从0重新计数。
    wangsheng@ubuntu[22:03:24]:~/Documents$ awk '{print FNR}' log student 
    1
    1
    2
    3
    4
    5
    

    处理多个文件

    1. 当要处理两个文件的时候,如何判断当前操作的是哪个文件呢?其实看了之前awk的内置变量,答案并不是很难,直接通过FNR与NR的关系就可以了。当操作的是第一个文件的时候,FNR==NR;当操作的是第二个文件的时候,FNR<NR。
    wangsheng@ubuntu[22:03:46]:~/Documents$ awk 'FNR==NR {print "this is first file"} FNR<NR {print "this is second file"}' log student 
    this is first file
    this is second file
    this is second file
    this is second file
    this is second file
    this is second file
    
    1. 当要处理的有三个文件的时候怎么办呢?上面这个办法显然不能解决问题,它只能判断是否是第一个文件,所以当判断三个或三个以上的文件的时候,可以使用FILENAME与ARGS数组,当FILENAME==ARGV[1]时,代表当前操作的是第一个文件,FILENAME==ARGV[2]的时候是第二个以此类推。
    wangsheng@ubuntu[22:17:21]:~/Documents$ awk 'FILENAME==ARGV[1] {print "1--"} FILENAME==ARGV[2] {print "2----"} FILENAME==ARGV[3] {print "3---"}' log student hello
    1--
    2----
    2----
    2----
    2----
    2----
    3---
    3---
    

    awk脚本

    还记得我们之前说awk -f选项的作用吗?看之前的代码也能看出来,awk的中间部分是很长的,如果每次都在命令行敲难免有些麻烦,将其放在文件中保存,使用的时候直接调用文件名,这样就很方便了,-f选项就是代表我写的awk中间的pattern {action}部分在后面的脚本文件中。
    首先创建一个awk脚本文件script.awk,注意文件开头是#!/bin/awk -f

    #!/bin/awk -f
    #运行前
    BEGIN {
        math = 0
        english = 0
        computer = 0
     
        printf "NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\n"
        printf "---------------------------------------------\n"
    }
    #运行中
    {
        math+=$3
        english+=$4
        computer+=$5
        printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
    }
    #运行后
    END {
        printf "---------------------------------------------\n"
        printf "  TOTAL:%10d %8d %8d \n", math, english, computer
        printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
    }
    

    脚本在BEGIN部分给三个变量赋值,接着打印一个表头;中间对每行进行累加第3、4、5列数值的操作;END部分将最终处理的值打印出来。
    运行脚本:

    //源文件
    wangsheng@ubuntu[22:30:01]:~/Documents$ cat student 
    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
    //运行后的结果
    wangsheng@ubuntu[22:30:03]:~/Documents$ awk -f script.awk student 
    NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL
    ---------------------------------------------
    Marry  2143     78       84       77      239
    Jack   2321     66       78       45      189
    Tom    2122     48       77       71      196
    Mike   2537     87       97       95      279
    Bob    2415     40       57       62      159
    ---------------------------------------------
      TOTAL:       319      393      350 
    AVERAGE:     63.80    78.60    70.00
    

    格式化输出

    格式化输出用于将数据按照我们想要的格式进行输出,而不是直接将数据打印出来。

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

    看几个例子:

    wangsheng@ubuntu[22:55:50]:~/Documents$ awk 'BEGIN{a=124.113;printf("%-10.2f",a)}'
    124.11    wangsheng@ubuntu[22:55:55]:~/Documents$
    

    %-10.2f f表示浮点数,.2表示小数部分保留两位,10表示总共占10个空白字符空间,-表示左对齐

    wangsheng@ubuntu[22:59:24]:~/Documents$ awk 'BEGIN{a=90;printf("%10X",a)}'
            5Awangsheng@ubuntu[22:59:31]:~/Documents$ 
    

    %10X X表示转为十六进制数,10表示总共占10个空白字符长度

    wangsheng@ubuntu[23:03:33]:~/Documents$ cat log 
    hello,world,java,linux,android
    wangsheng@ubuntu[23:03:35]:~/Documents$ awk -F , '{printf("%-10s---%10s---%-10s---%10s",$1,$2,$3,$4)}' log
    hello     ---     world---java      ---     linuxwangsheng@ubuntu[23:03:38]:~/Documents$ 
    

    将log文件根据,分割开,分别对第1、2、3、4列打印输出,有的左对齐,有的右对齐,均占10个空白字符

    参考资料

    http://man.linuxde.net/awk
    http://www.runoob.com/linux/linux-comm-awk.html
    http://www.mamicode.com/info-detail-1594530.html

    相关文章

      网友评论

          本文标题:linux命令学习(三)——awk

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