zoukankan      html  css  js  c++  java
  • 文本处理之awk

    基本语法

    awk通过FS作为每一段文本的分割符(默认空格),在命令行上可以用-F参数指定分隔符;

    通过RS参数指定文本换行符(默认回车,所以是一行行取数据的),通过换行符作为分割来读取文件。这里区别于sedsed是一行一行读取文件的

    常用内置变量:

    ​ $0 当前所有字段

    ​ $1--$n 按照分隔符分割取到的第n列内容

    ​ FS 分隔符(默认空格) awk 'BEGIN{FS=":"}{print $1}' /etc/passwd ;等价于awk -F: '{print $1}' /etc/passwd

    ​ RS 换行符(默认回车) awk 'BEGIN{RS=":"}{print $1}' /etc/passwd

    ​ NF 字符列数,当前处理行的分割后的列数 awk -F: '{print NF}' /etc/passwd

    ​ NR 行号 awk -F: '{print NR ":" $1}' /etc/passwd

    ​ OFS (默认空格)输出字段分隔符

    ​ ORS (默认回车)输出记录分隔符

    自定义外部变量

    ​ -v:自定义变量

    awk -v host=$HOSTNAME "BEGIN{print host}"

    关系操作符

    <、>、<=、>=、==、!=、、!

    ~:用来判断前面的列是否匹配后面的内容。例如awk -F: '$7 ~ /^/bin/{print $0}' /etc/passwd(判断第7列是否以/bin开头,如果是打印该列)

    !~:不匹配

    输出

    print与printf

    print:直接输出

    $  awk -F: '{print $1 ":" $2}' /etc/passwd
    

    printf:格式化输出

    $  awk  -F: '{printf "hello %s, your path is %s
    ",$1,$NF}' /etc/passwd
    hello root, your path is /bin/bash
    hello bin, your path is /sbin/nologin
    hello daemon, your path is /sbin/nologin
    hello adm, your path is /sbin/nologin
    hello lp, your path is /sbin/nologin
    ....
    

    注意:printf需要手动增加 来换行。使用%s来格式化,printf外加入要替换的变量

    awk的流程控制

    if语句 if(expression){action1}else{action2}

    1. 产生10个数seq 10,通过if语句判断是单数还是双数
    $  seq 10 |awk '{if($0%2==0){print $0"是双数"}else{print $0"是单数"}}'
    1是单数
    2是双数
    3是单数
    4是双数
    5是单数
    6是双数
    7是单数
    8是双数
    9是单数
    10是双数
    
    1. 如果只需要一个if分支,可以省略前面的if
    $  awk -F: '$3<10{printf"用户名 -> %s %s uid --> %s
    ", $1,"-->",$3}' /etc/passwd
    用户名 -> root --> uid --> 0
    用户名 -> bin --> uid --> 1
    用户名 -> daemon --> uid --> 2
    用户名 -> adm --> uid --> 3
    用户名 -> lp --> uid --> 4
    用户名 -> sync --> uid --> 5
    用户名 -> shutdown --> uid --> 6
    用户名 -> halt --> uid --> 7
    用户名 -> mail --> uid --> 8
    
    $  awk -F: '{if($3<10)printf"用户名 -> %s %s uid --> %s
    ", $1,"-->",$3}' /etc/passwd
    用户名 -> root --> uid --> 0
    用户名 -> bin --> uid --> 1
    用户名 -> daemon --> uid --> 2
    用户名 -> adm --> uid --> 3
    用户名 -> lp --> uid --> 4
    用户名 -> sync --> uid --> 5
    用户名 -> shutdown --> uid --> 6
    用户名 -> halt --> uid --> 7
    用户名 -> mail --> uid --> 8
    

    循环

    while

    while(expression){action}

    例子:使用:分割/etc/passwd,并将每一列前加上列号

    $  awk -F: '{i=1;while(i<=NF){print i":"$i;i++}}' /etc/passwd
    1:root
    2:x
    3:0
    4:0
    5:root
    6:/root
    7:/bin/bash
    ....
    

    设置变量i初始值为1, while循环每次循环自增1, 设定条件i的值永远小于字段数, awk首先会读一行, 然后会以:为分隔符读取每一列数据, NF表示字段的总数, 最后打印i$i (i表示循环每一列的列好, $i表示每一列的值)

    for

    1. for(i=0;i<=10;i++){action}

    分割/etc/passwd,并将每一列前加上列号

    $  awk -F: '{for(i=1;i<=NF;i++){print i":"$i}}' /etc/passwd
    1:root
    2:x
    3:0
    4:0
    5:root
    6:/root
    7:/bin/bash
    
    1. for(value in array){action}

    当value在array的key中,进行下面的操作。awk的数组类似python中的字典。

    统计/etc/passwd第7列的值及对应的个数

    $  awk -F: '{a[$7]++}END{for(i in a)if(i!=""){print i":"a[i]}}' /etc/passwd
    /bin/sync:1
    /bin/bash:2
    /sbin/nologin:16
    /sbin/halt:1
    /sbin/shutdown:1
    

    创建数组a,以第7列作为下标,使用运算符++作为数组元素,元素初始值为0。处理/etc/passwd第一行时,下标是某个路径(/bin/bash),元素加1,处理第二行时,下标又是一个路径(/sbin/nologin),元素加1,如果这个路径已经存在在数组a中,则a[/sbin/nologin]的数组元素加1,也就是这个路径出现了两次,元素结果是2,以此类推。因此可以实现去重,统计出现次数。

    核心语句放在END因为需要将数据收集完后才取统计这些数据

    数组

    array[1]="hello"

    array["name"]="Jack"

    数组类似python的字典,array[key值]="value值";key为索引,可以是数字也可以是字符串。

    数组元素的删除:delete array["key"]

    例子:定义了数组a的三个值,并打印结果查看

    $  awk 'BEGIN{a[1]="hello";a[2]="word";a["name"]="meitian";for(i in a){print "key为"i":value为"a[i]}}'
    key为name:value为meitian
    key为1:value为hello
    key为2:value为word
    

    函数

    更多

    split

    把一个字符串分隔为单词并存储在数组中

    格式:

    split (string, array, field separator)
    split (string, array) -->如果第三个参数没有提供,awk就默认使用当前FS值。

    $  time=`date |awk '{print $5}'`
    $  echo $time
    20:40:15
    $  echo $time|awk '{split($0,a,":");print a[1],a[2],a[3]}'
    20 40 15
    

    substr

    截取字符串

    返回从起始位置起,指定长度之子字符串;若未指定长度,则返回从起始位置到字符串末尾的子字符串。
    格式:
    substr(s,p)返回字符串s中从p开始的后缀部分
    substr(s,p,n)返回字符串s中从p开始长度为n的后缀部分

    $  echo "hhp love a man" |awk '{print substr($2,1,4)}'
    love
    $  echo "hhp love a man" |awk '{print substr($0,1,2)}'
    hh
    $  echo "hhp love a man" |awk '{print substr($0,1,3)}'
    hhp
    

    length

    字符串长度

    length函数返回没有参数的字符串的长度。length函数返回整个记录中的字符数

    $  echo "hhp love a man" |awk '{print length}'
    14
    

    gsub

    gsub函数则使得在所有正则表达式被匹配的时候都发生替换。

    gsub(regular expression,subsitution string,target string);简称gsub (r,s,t)

    把一个文件里面所有包含abc的行里面的abc替换成def,然后输出第一列和第三列

    $  echo "hhp love a man" |awk '$0 ~/hhp/{gsub("hhp","hhpsb");print $0}'
    hhpsb love a man
    

    next

    在awk中,如果调用next,那么next之后的命令就都在执行了,此行文本的处理到此结束,那么读取下一条记录并操作

    $  cat test.txt 
    Tom    2012-12-11      car     53000
    John   2013-01-13      bike    41000
    vivi    2013-01-18      car     42800
    Tom    2013-01-20      car     32500
    John   2013-01-28      bike    63500
    $  awk '{if(NR==1){next}print $0}' test.txt 
    John   2013-01-13      bike    41000
    vivi    2013-01-18      car     42800
    Tom    2013-01-20      car     32500
    John   2013-01-28      bike    63500
    

    基本的实例

    素材准备

    $ cat netstat.txt 
    Proto Recv-Q Send-Q  Local-Address Foreign-Address  State
    tcp        0      0  0.0.0.0:3306      0.0.0.0:*    LISTEN
    tcp        0      0  0.0.0.0:80        0.0.0.0:*    LISTEN
    tcp        0      0  127.0.0.1:9000    0.0.0.0:*     LISTEN
    tcp        0      0  coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
    tcp        0      0  coolshell.cn:80  61.140.101.185:37538 FIN_WAIT2
    tcp        0      0  coolshell.cn:80 11.194.134.189:1032 ESTABLISHED
    tcp        0      0 coolshell.cn:80 123.169.124.11:49809 ESTABLISHED
    tcp        0      0 coolshell.cn:80   116.234.127.77:11502 FIN_WAIT2
    tcp        0      0 coolshell.cn:80 12.16.124.111:49829  ESTABLISHED
    tcp        0      0 coolshell.cn:80   183.60.215.36:36970  TIME_WAIT
    tcp        0   4166 coolshell.cn:80   61.148.24.38:30901 ESTABLISHED
    tcp        0      1 coolshell.cn:80 12.152.181.209:26825   FIN_WAIT1
    tcp        0      0 coolshell.cn:80   110.194.134.189:47 ESTABLISHED
    

    输出第1列和第4例

    $ awk {'print $1,$3'} netstat.txt
    

    其中单引号中的被大括号括着的就是awk的语句,注意,其只能被单引号包含
    其中的$1..$n表示第几例。注:$0表示整个行

    补充

    如果换成双引号, awk语法就不起作用了

    • 输入$1-$9,都会显示文件中的所有内容

      $ awk {"print $1"} netstat.txt 
      Proto Recv-Q Send-Q  Local-Address Foreign-Address  State
      tcp        0      0  0.0.0.0:3306      0.0.0.0:*    LISTEN
      tcp        0      0  0.0.0.0:80        0.0.0.0:*    LISTEN
      tcp        0      0  127.0.0.1:9000    0.0.0.0:*     LISTEN
      tcp        0      0  coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
      tcp        0      0  coolshell.cn:80  61.140.101.185:37538 FIN_WAIT2
      tcp        0      0  coolshell.cn:80 11.194.134.189:1032 ESTABLISHED
      tcp        0      0 coolshell.cn:80 123.169.124.11:49809 ESTABLISHED
      tcp        0      0 coolshell.cn:80   116.234.127.77:11502 FIN_WAIT2
      tcp        0      0 coolshell.cn:80 12.16.124.111:49829  ESTABLISHED
      tcp        0      0 coolshell.cn:80   183.60.215.36:36970  TIME_WAIT
      tcp        0   4166 coolshell.cn:80   61.148.24.38:30901 ESTABLISHED
      tcp        0      1 coolshell.cn:80 12.152.181.209:26825   FIN_WAIT1
      tcp        0      0 coolshell.cn:80   110.194.134.189:47 ESTABLISHED
      
    • 输入$0就会输出0, 文件中有多少行就输出多少行0

      awk {"print $0"} netstat.txt 
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      
    • 输入$10以后的数字就会输出的值和echo $10是一样的, 文件中有多少行就输出多少行0

      $ awk {"print $10"} netstat.txt 
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      $ echo $10
      0
      
      • echo $10规律

        会首先输出echo $1, 然后在输出后面的数字

      • echo $01规律:

        会首先输出echo $0, 然后在输出后面的数字

      $ echo $10
      0
      $ echo $11
      1
      $ echo $19
      9
      $ echo $190
      90
      $ echo $01
      -bash1
      $ echo $1
      
      $ echo $01ssdsd
      -bash1ssdsd
      

    显示第一列和最后一列

    $  awk  '{print $1,"-->",$NF}' netstat.txt
    

    这里$NF来表示最后一列

    过滤记录

    1. 过滤第三列的值为0 && 第6列的值为LISTEN
    $ awk '{print $3==0,$6}' netstat.txt
    

    其中的==为比较运算符。其他比较运算符:!=, <, < >=

    1. 过滤第三列的值为0, 并且打印对应的第6列的信息
    $ awk '{print $3==0,$6}' netstat.txt
    
    1. 打印第三列的值大于0的第一列和第5列的信息
    $ awk '$3>0{print $1,$5}' netstat.txt
    
    1. 打印uid>10的用户名和uid
    $  awk -F: '$3<10{printf"用户名 -> %s %s uid --> %s
    ", $1,"-->",$3}' /etc/passwd
    用户名 -> root --> uid --> 0
    用户名 -> bin --> uid --> 1
    用户名 -> daemon --> uid --> 2
    用户名 -> adm --> uid --> 3
    用户名 -> lp --> uid --> 4
    用户名 -> sync --> uid --> 5
    用户名 -> shutdown --> uid --> 6
    用户名 -> halt --> uid --> 7
    用户名 -> mail --> uid --> 8
    
    $  awk -F: '{if($3<10)printf"用户名 -> %s %s uid --> %s
    ", $1,"-->",$3}' /etc/passwd
    用户名 -> root --> uid --> 0
    用户名 -> bin --> uid --> 1
    用户名 -> daemon --> uid --> 2
    用户名 -> adm --> uid --> 3
    用户名 -> lp --> uid --> 4
    用户名 -> sync --> uid --> 5
    用户名 -> shutdown --> uid --> 6
    用户名 -> halt --> uid --> 7
    用户名 -> mail --> uid --> 8
    

    指定分隔符

    $ awk -F: '{print $1,$6,$7}' /etc/passwd  # 输出用户名,家目录,解释器
    

    -F的意思就是指定分隔符

    注:如果你要指定多个分隔符,你可以这样来:
    awk -F '[;:]'
    

    作为分隔符输出

    $ awk -F: '{print $1, $6, $7}' OFS='	' /etc/passwd   # 输出用户名,家目录,解释器, 三列数据之间使用	相隔
    

    OFS表示的是分隔符

    内建变量

    awk '{print NR, "->", $1, $6, $7, "字段数","=>",NF}' OFS='	' netstat.txt
    

    NR(Number of Record) 表示的是已经处理过的总记录数目,或者说行号(不一定是一个文件,可能是多个)

    NF(Number for Field)表示的是,一条记录的字段的数目.

    字符串匹配

    ~ 表示模式开始 | / /是模糊搜索的模式 | !~ 表示模式取反

    1. 匹配第6列带有FIN的记录
    $  awk '$6~/FIN/{print NR,$4,$5,$6}' netstat.txt
    
    1. 匹配第6列带有FIN和TIME的记录
    $  awk '$6~/FIN|TIME/ {print NR,$4,$5,$6}' netstat.txt 
    5 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
    6 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2
    9 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2
    11 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT
    13 coolshell.cn:80 12.152.181.209:26825 FIN_WAIT1
    
    1. 匹配第6行不带FIN的记录
    $  awk '$6~/FIN/ {print NR,$4,$5,$6}' netstat.txt
    
    1. 匹配最后一列是/sbin/nologin的数据
    $  awk '$NF~//bin/bash/' /etc/passwd 
    root:x:0:0:root:/root:/bin/bash
    java:x:1000:1000:java:/home/java:/bin/bash
    
    1. grep一样的去匹配
    $  grep 'LISTEN' netstat.txt 
    tcp        0      0  0.0.0.0:3306      0.0.0.0:*    LISTEN
    tcp        0      0  0.0.0.0:80        0.0.0.0:*    LISTEN
    tcp        0      0  127.0.0.1:9000    0.0.0.0:*     LISTEN
    $  awk '/LISTEN/' netstat.txt 
    tcp        0      0  0.0.0.0:3306      0.0.0.0:*    LISTEN
    tcp        0      0  0.0.0.0:80        0.0.0.0:*    LISTEN
    tcp        0      0  127.0.0.1:9000    0.0.0.0:*     LISTEN
    
    $  awk '/LIST/ {print NR,$0}' netstat.txt 
    2 tcp        0      0  0.0.0.0:3306      0.0.0.0:*    LISTEN
    3 tcp        0      0  0.0.0.0:80        0.0.0.0:*    LISTEN
    4 tcp        0      0  127.0.0.1:9000    0.0.0.0:*     LISTEN
    $  grep -n 'LIST' netstat.txt 
    2:tcp        0      0  0.0.0.0:3306      0.0.0.0:*    LISTEN
    3:tcp        0      0  0.0.0.0:80        0.0.0.0:*    LISTEN
    4:tcp        0      0  127.0.0.1:9000    0.0.0.0:*     LISTEN
    

    拆分文件

    awk输出的内容重定向到一个文件

    $  awk 'NR!=1{print $6}' netstat.txt > col6.txt
    $  cat col6.txt 
    

    统计

    统计/etc/passwd第7列的值及对应的个数

    $  awk -F: '{a[$7]++}END{for(i in a)if(i!=""){print i":"a[i]}}' /etc/passwd
    /bin/sync:1
    /bin/bash:2
    /sbin/nologin:16
    /sbin/halt:1
    /sbin/shutdown:1
    

    创建数组a,以第7列作为下标,使用运算符++作为数组元素,元素初始值为0。处理/etc/passwd第一行时,下标是某个路径(/bin/bash),元素加1,处理第二行时,下标又是一个路径(/sbin/nologin),元素加1,如果这个路径已经存在在数组a中,则a[/sbin/nologin]的数组元素加1,也就是这个路径出现了两次,元素结果是2,以此类推。因此可以实现去重,统计出现次数。

    核心语句放在END因为需要将数据收集完后才取统计这些数据

    统计各个connection

    $  awk 'NR!=1{a[$6]++;} END {for (i in a) print i ", " a[i];}' netstat.txt
    LISTEN, 3
    ESTABLISHED, 5
    FIN_WAIT1, 1
    FIN_WAIT2, 2
    TIME_WAIT, 2
    

    统计每个用户的进程的占了多少内存

    $  ps aux | awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}'
    chrony, 2084KB
    dbus, 2596KB
    polkitd, 13968KB
    postfix, 8156KB
    root, 127468KB
    

    创建数组a,以第1列作为下标,使用运算符+=$6作为数组元素,元素初始值为0。处理ps aux第一行时,下标是某个用户, 值是这个用户所在行的第六列, 元素的值加上第六列的值, 这样以此类推, 可以将每个用户所起的进程的总内存进行统计

    核心语句放在END因为需要将数据收集完后才取统计这些数据

    ps aux每一列参数解释

    [root@git ~]# ps aux
    USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root          1  0.1  0.6 127968  6552 ?        Ss   09:23   0:01 /usr/lib/systemd/systemd --switched-root --system --deseri
    root          2  0.0  0.0      0     0 ?        S    09:23   0:00 [kthreadd]
    root          3  0.0  0.0      0     0 ?        S    09:23   0:00 [ksoftirqd/0]
    root          5  0.0  0.0      0     0 ?        S<   09:23   0:00 [kworker/0:0H]
    root          6  0.0  0.0      0     0 ?        S    09:23   0:00 [kworker/u256:0]
    root          7  0.0  0.0      0     0 ?        S    09:23   0:00 [migration/0]
    root          8  0.0  0.0      0     0 ?        S    09:23   0:00 [rcu_bh]
    root          9  0.0  0.0      0     0 ?        R    09:23   0:00 [rcu_sched]
    root         10  0.0  0.0      0     0 ?        S<   09:23   0:00 [lru-add-drain]
    ...
    

    USER: 运行进程的用户

    PID 进程的ID号

    %CPU 进程占用CPU的百分比

    %MEM 进程占用内存的百分比

    VSZ 进程占用虚拟内存的百分比

    RSS 占用固定内存的百分比

    TTY 进程在哪个终端运行, 如果和终端无关, 会显示?; 如果显示pts/0, 则表示通过网络连接主机并运行进程

    STAT 进程的运行状态, linux下一共有5种状态

    • D 不可中断 uninterruptible sleep (usually IO)
    • R 运行 runnable (on run queue)
    • S 中断 sleeping
    • T 停止 traced or stopped
    • Z 僵尸 a defunct (”zombie”) process

    START 进程的开始时间

    TIME 进程的执行时间

    COMMAND 进程的执行路径

    函数实例

    素材准备

    Tom    2012-12-11      car     53000
    John   2013-01-13      bike    41000
    vivi    2013-01-18      car     42800
    Tom    2013-01-20      car     32500
    John   2013-01-28      bike    63500
    

    split

    输出一月份每个员工的工资:

    $  awk '{split($2,a,"-");if(a[2]=01){sum[$1]+=$4}}END{for(i in sum) print sum[i],i}' test.txt 
    42800 vivi
    85500 Tom  
    104500 John 
    

    gsub

    /sbin/nologin替换成/bin/bash

    $  cp /etc/passwd passwd
    
    $  awk -F: '$NF~//sbin/nologin/{print $1,"-->",$NF}' passwd
    bin --> /sbin/nologin
    daemon --> /sbin/nologin
    adm --> /sbin/nologin
    lp --> /sbin/nologin
    mail --> /sbin/nologin
    operator --> /sbin/nologin
    games --> /sbin/nologin
    ftp --> /sbin/nologin
    nobody --> /sbin/nologin
    systemd-network --> /sbin/nologin
    dbus --> /sbin/nologin
    polkitd --> /sbin/nologin
    tss --> /sbin/nologin
    sshd --> /sbin/nologin
    postfix --> /sbin/nologin
    chrony --> /sbin/nologin
    
    $  awk -F: '{gsub("/sbin/nologin","/bin/bash")}$NF~//bin/bash/{print $1,"-->",$NF}' passwd 
    root --> /bin/bash
    bin --> /bin/bash
    daemon --> /bin/bash
    adm --> /bin/bash
    lp --> /bin/bash
    mail --> /bin/bash
    operator --> /bin/bash
    games --> /bin/bash
    ftp --> /bin/bash
    nobody --> /bin/bash
    systemd-network --> /bin/bash
    dbus --> /bin/bash
    polkitd --> /bin/bash
    tss --> /bin/bash
    sshd --> /bin/bash
    postfix --> /bin/bash
    chrony --> /bin/bash
    java --> /bin/bash
    

    核心代码段是 {gsub("/sbin/nologin","/bin/bash")}, 将passwd文件中每一行中的 /sbin/nologin替换成 /bin/bash

    $NF~//bin/bash/{print $1,"-->",$NF} 这段代码就是将处理好的文件进行过滤, 将文件中/bin/bash的字符串匹配出来, 并输出第一列和最后一列

    substr

    $  echo "hhp love a man" |awk '{print substr($2,1,4)}'
    love
    $  echo "hhp love a man" |awk '{print substr($0,1,2)}'
    hh
    $  echo "hhp love a man" |awk '{print substr($0,1,3)}'
    hhp
    

    length

    $  echo "hhp love a man" |awk '{print length}'
    14
    

    next

    $  cat test.txt 
    Tom    2012-12-11      car     53000
    John   2013-01-13      bike    41000
    vivi    2013-01-18      car     42800
    Tom    2013-01-20      car     32500
    John   2013-01-28      bike    63500
    $  awk '{if(NR==1){next}print $0}' test.txt 
    John   2013-01-13      bike    41000
    vivi    2013-01-18      car     42800
    Tom    2013-01-20      car     32500
    John   2013-01-28      bike    63500
    
  • 相关阅读:
    yuv文件并行解析播放
    视频解析
    有意思的并查集讲解 收藏
    C++输入输出重载
    python 同步IO
    多线程与多进程的理解
    centos7 配置redis
    linux中的raid
    form表单系列中文件上传及预览
    centos7 安装swftools Apache_OpenOffice
  • 原文地址:https://www.cnblogs.com/cjwnb/p/11608804.html
Copyright © 2011-2022 走看看