SElinux

awk介绍

  • awk:Aho, Weinberger, Kernighan,报告生成器,格式化文本输出
  • 有多种版本: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 ...
BEGIN是文件一行没有读进来的时候要做的事情或要执行的动作
END当整个文件都处理完之后,要执行的动作比
pattern是每一行要处理的动作
awk 程序通常由:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成
program通常是被单引号或双引号中

选项
-F 指明输入时用到的字段分隔符
-v var=value: 自定义变量

awk语言


  • 基本格式: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是读入一行,会按照定义的分隔符,切割成字段,分隔符可以用-F来定义,分隔完后这些字段会有名字比如第一个字段是 12 2, 3, 后面跟第几个字段,也可以当 1 3字段,也可循环从 1 1为大于100时,就取中间$3,如果小于100则不做处理,读取下一行

范例

  • 如果awk不写program则是整行都处理,所以会读入一行执行一个print这个动作
  • awk默认是读取标准输出的,所以我们可以用管道传给awk

  • 这里一定要注意,只要是字符串一定要拿双引号引起来
  • 也可以做数字运算,数字不用加双引号
  • 取出/etc/passwd文件的第3列
  • 取出磁盘利用率也是很方便的,awk默认是以空格为分隔符的。也可取多个字段,也可自定义格式
  • [root@centos6 app]# df | grep /dev/sd| awk '{print $1,$5}'  
    /dev/sda2 10%
    /dev/sda3 1%
    /dev/sda1 4%
    [root@centos6 app]# df | grep /dev/sd| awk '{print $1"===="$5}'  
    /dev/sda2====10%
    /dev/sda3====1%
    /dev/sda1====4%

    awk工作原理

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

    awk格式

    • print格式: print item1, item2, …
    • 要点
      1. 逗号分隔符
      2. 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式
      3. 如省略item,相当于print $0
    • 示列
    awk '{print "hello,awk"}'
    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变量

    • 变量:内置和自定义变量
    • 在使用变量时,前面加-v表示对变量赋值
    • FS:输入字段分隔符,默认为空白字符
    awk -v FS=':' '{print $1,FS,$3}' /etc/passwd
    awk –F: '{print $1,$3,$7}' /etc/passwd
    这两种写法是等价的
    因为FS是一个变量,所有我们可以引用这个变量,而-F是一个选项,不能被引用
    [root@centos6 app]# awk -v FS=":" '{print $1,FS,$4}' /etc/passwd
    root : 0
    bin : 1
    daemon : 2
    adm : 4
    lp : 7
    sync : 0
    shutdown : 0
    halt : 0
    mail : 12
    uucp : 14
    operator : 0
    games : 100
    gopher : 30
    ftp : 50
    nobody : 99
    dbus : 81
    usbmuxd : 113
    FS也可以引用bash变量
    [root@centos6 app]# i=":" ; awk -v FS="$i" '{ print $1,FS,$3 }' /etc/passwd
    root : 0
    bin : 1
    daemon : 2
    adm : 3
    lp : 4
    sync : 5
    shutdown : 6
    halt : 7
    mail : 8
    -F也是可以调用shell变量的,只是里面不能使用了
    [root@centos6 app]# i=":" ; awk -F "$i" '{ print $1,:,$3 }' /etc/passwd
    • OFS:输出字段分隔符,默认为空白字符
    awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd
    逗号默认输出是空格为分隔符,我们也可以定义显示的分隔符
    [root@centos6 app]# awk -v FS=":" -v OFS="+" '{print $1,$3}' /etc/passwd
    root+0
    bin+1
    daemon+2
    adm+3
    lp+4
    sync+5
    shutdown+6
    halt+7
    mail+8
    • RS:输入记录分隔符,指定输入时的换行符,默认是以回车换行为一行的分隔符。当然我们也可以指定行分隔符
    awk -v RS=' ' '{print }' /etc/passwd
    比如,
    vim f1.txt
    1 2 3;4 5 6;7
    8 9
    如果我们以分号为分隔符,这这里就是3行记录
    [root@centos6 app]# awk -v RS=";" '{print $2}' f1.txt 
    2
    5
    8
    默认是以空白符分隔字段,我们打印第2个字段所有是258是三行记录
    ===================
    [root@centos6 app]# cat f2 
    a
    b
    c dd
    ff
    gg aaa
    qq
    ww
    eee ccc
    这文件如果我们以空格为记录分隔符,取第1个字段和第2个字段的结果是什么
    [root@centos6 app]# awk -v RS=" " '{print $1,$2}' f2
    a b
    dd ff
    aaa qq
    ccc 
    结果是这样的,所有回车换行并不是空白符
    • ORS:输出记录分隔符,输出时用指定符号代替换行符
    • 默认输出的时候是以回车换行为分隔符
    awk -v RS=' ' -v ORS='###''{print }' /etc/passwd
    [root@centos6 app]# awk -v ORS="====" -v RS=" " '{print $1,$2}' f2
    a b====dd ff====aaa qq====ccc ====[root@centos6 app]# 
    • NF:字段数量
    awk -F: '{print NF}' /etc/fstab,引用内置变量不用$
    awk -F: '{print $(NF-1)}' /etc/passwd
    [root@centos6 app]# awk -F : '{print NF}' /etc/passwd
    7
    7
    7
    7
    7
    [root@centos6 app]# awk -F : '{print $NF}' /etc/passwd   $NF表示最后一个字段
    /bin/bash
    /sbin/nologin
    /sbin/nologin
    /sbin/nologin
    /sbin/nologin
    [root@centos6 app]# awk -F : '{print $(NF-1)}' /etc/passwd  
    /root
    [root@centos6 app]# df |awk '{print $(NF-1)}'|awk -F % '{print $1}'
    Mounted
    10
    1
    1
    4
    100
    [root@centos6 app]# 
    • NR:记录号,行号,空行也算
    awk '{print NR}' /etc/fstab ; awk END'{print NR}' /etc/fstab
    [root@centos6 app]# awk '{print NR $0}' /etc/fstab 
    1
    2#
    3# /etc/fstab
    4# Created by anaconda on Tue Nov  7 15:42:36 2017
    5#
    6# Accessible filesystems, by reference, are maintained under '/dev/disk'
    7# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
    8#
    9UUID=03352018-7cef-4ee3-9a05-b0833b67da19 /                       ext4    defaults        1 1
    10UUID=0bd51fa6-f577-427a-b0b2-1cb8a631ac16 /app                    ext4    defaults        1 2
    11UUID=bbfd63dd-da18-4ac9-affd-4724c571db21 /boot                   ext4    defaults        1 2
    12UUID=a8c19a79-3c21-4aa4-a648-27a069f25bde swap                    swap    defaults        0 0
    也可以后面跟多个文件,awk会认为把多个文件当成一个文件开始计算行号
    [root@centos6 app]# awk '{print NR $0}' /etc/fstab  /etc/issue
    1
    2#
    3# /etc/fstab
    4# Created by anaconda on Tue Nov  7 15:42:36 2017
    5#
    6# Accessible filesystems, by reference, are maintained under '/dev/disk'
    7# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
    8#
    9UUID=03352018-7cef-4ee3-9a05-b0833b67da19 /                       ext4    defaults        1 1
    10UUID=0bd51fa6-f577-427a-b0b2-1cb8a631ac16 /app                    ext4    defaults        1 2
    11UUID=bbfd63dd-da18-4ac9-affd-4724c571db21 /boot                   ext4    defaults        1 2
    12UUID=a8c19a79-3c21-4aa4-a648-27a069f25bde swap                    swap    defaults        0 0
    13tmpfs                   /dev/shm                tmpfs   defaults        0 0
    14devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
    15sysfs                   /sys                    sysfs   defaults        0 0
    16proc                    /proc                   proc    defaults        0 0
    17/dev/cdrom    /mnt    iso9660         defaults        0       0
    18CentOS release 6.9 (Final)
    19Kernel \r on an \m
    20
    • FNR:各文件分别计数,记录号
    • 因为NR是会把多个文件当成一个统计,所有FNR会把文件单独统计
    awk '{print FNR}' /etc/fstab /etc/inittab
    [root@centos6 app]# awk '{print FNR $0}' /etc/fstab /etc/issue
    1
    2#
    3# /etc/fstab
    4# Created by anaconda on Tue Nov  7 15:42:36 2017
    5#
    6# Accessible filesystems, by reference, are maintained under '/dev/disk'
    7# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
    8#
    9UUID=03352018-7cef-4ee3-9a05-b0833b67da19 /                       ext4    defaults        1 1
    10UUID=0bd51fa6-f577-427a-b0b2-1cb8a631ac16 /app                    ext4    defaults        1 2
    11UUID=bbfd63dd-da18-4ac9-affd-4724c571db21 /boot                   ext4    defaults        1 2
    12UUID=a8c19a79-3c21-4aa4-a648-27a069f25bde swap                    swap    defaults        0 0
    13tmpfs                   /dev/shm                tmpfs   defaults        0 0
    14devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
    15sysfs                   /sys                    sysfs   defaults        0 0
    16proc                    /proc                   proc    defaults        0 0
    17/dev/cdrom    /mnt    iso9660         defaults        0       0
    1CentOS release 6.9 (Final)
    2Kernel \r on an \m
    3
    • FILENAME:显示当前文件名
    awk '{print FILENAME}' /etc/fstab
    [root@centos6 app]# awk '{print FILENAME,$0}' /etc/issue 
    /etc/issue CentOS release 6.9 (Final)
    /etc/issue Kernel \r on an \m
    /etc/issue 
    • ARGC:命令行参数的个数
    awk '{print ARGC}' /etc/fstab /etc/inittab
    awk 'BEGIN {print ARGC}' /etc/fstab /etc/inittab
    [root@centos6 app]# awk '{print ARGC}' /etc/fstab /etc/issue    
    3
    3
    3
    3
    3
    • ARGV:数组,保存的是命令行所给定的各参数
    awk 'BEGIN {print ARGV[0]}' /etc/fstab /etc/inittab
    awk 'BEGIN {print ARGV[1]}' /etc/fstab /etc/inittab
    [root@centos6 app]# awk '{print ARGV[0]}' /etc/fstab /etc/issue  
    awk
    awk
    awk
    awk
    • 自定义变量(区分字符大小写)
      1. -v var=value
      2. 在program中直接定义
      3. 变量是先定义在调用
    • 示例
    awk -v test='hello gawk' '{print test}' /etc/fstab
    awk -v test='hello gawk' 'BEGIN{print test}'
    awk 'BEGIN{test="hello,gawk";print test}'
    awk –F:'{sex="male";print $1,sex,age;age=18}' /etc/passwd
    ======也可将常用的程序写到文件中用-f调用======
    cat awkscript 
    {print script,$1,$2}
    awk -F: -f awkscript script="awk" /etc/passwd
    [root@centos6 app]# cat awk.txt 
    {user="usernam";uid="uid";print user"="$1,uid"="$3}
    [root@centos6 app]# awk -F: -f awk.txt /etc/passwd
    usernam=root uid=0
    usernam=bin uid=1
    usernam=daemon uid=2
    usernam=adm uid=3
    usernam=lp uid=4
    ==========
    [root@centos6 app]# awk -v user="username" -v uid="uid" -F: '{print user"="$1,uid"="$3}' /etc/passwd
    username=root uid=0
    username=bin uid=1
    username=daemon uid=2
    username=adm uid=3
    username=lp uid=4
    username=sync uid=5
    username=shutdown uid=6
    [root@centos6 app]# awk  -v uid="uid" -F: '{user="username";print user"="$1,uid"="$3}' /etc/passwd     //也可以在程序里定义变量,在程序里定义变量就不能调用shell里的变量               
    username=root uid=0
    username=bin uid=1
    username=daemon uid=2
    username=adm uid=3
    username=lp uid=4
    username=sync uid=5
    username=shutdown uid=6
    username=halt uid=7
    userame=mail uid=8
    username=uucp uid=10
    [root@centos6 app]# u=username; awk -v user=$u -v uid=uid -F: '{print user"="$1,uid"="$3}' /etc/passwd  
    username=root uid=0
    username=bin uid=1
    username=daemon uid=2
    username=adm uid=3

    printf命令


    • 格式化输出:printf “FORMAT”, item1, item2, …
      1. 必须指定FORMAT
      2. 不会自动换行,需要显式给出换行控制符,\n
      3. FORMAT中需要分别为后面每个item指定格式符
      4. 格式和数据要以逗号隔开

    格式符:与item一一对应
    %c: 显示字符的ASCII码
    %d, %i: 显示十进制整数
    %e, %E:显示科学计数法数值
    %f:显示为浮点数
    %g, %G:以科学计数法或浮点形式显示数值
    %s:显示字符串
    %u:无符号整数
    %%: 显示%自身
  • 修饰符
  • #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
    -: 左对齐(默认右对齐) %-15s
    +:显示数值的正负符号 %+d

    printf示例

    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
    [root@centos6 app]# awk -F: '{printf "%s:%d\n",$1,$3}' /etc/passwd
    root:0
    bin:1
    daemon:2
    adm:3
    lp:4
    sync:5
    shutdown:6
    halt:7
    mail:8
    uucp:10
    operator:11
    [root@centos6 app]# awk -F: '{printf "%-30s%d\n",$1,$3}' /etc/passwd 
    root                          0
    bin                           1
    daemon                        2
    adm                           3
    lp                            4
    sync                          5
    shutdown                      6
    halt                          7
    [root@centos6 app]# awk -F: '{printf "%-30s| %d\n",$1,$3}' /etc/passwd
    root                          | 0
    bin                           | 1
    daemon                        | 2
    adm                           | 3
    lp                            | 4
    sync                          | 5
    shutdown                      | 6
    halt                          | 7
    
    [root@centos6 app]# awk -v n=123.4567 '{printf "%8.4f\n",n }'  /etc/fstab  //这里n的值加小数点一共8位,所有我们第一个8是总长度,小数点之后的是4位
    123.4567
    123.4567
    123.4567
    123.4567
    123.4567
    [root@centos6 app]# awk -v n=123.4567 '{printf "%8.3f\n",n }' /etc/fstab  这个意思是小数点之后是3位说以最后一位7被四舍五入了,但是前面多一个空格,代表一共是8位
     123.457
     123.457
     123.457
     123.457
     123.457
     123.457
     123.457
    因为这里打印多行是文件有几行就打印几行,我们可以用BEGIN,可以不用跟文件就打印一次
    [root@centos6 app]# awk -v n=123.4567 'BEGIN{printf "%8.3f\n",n }'
     123.457
    [root@centos6 app]# awk -v n=123.4567 'BEGIN{printf "%10.3f\n",n }' 
       123.457

    操作符

    • 算术操作符
    x+y, x-y, x*y, x/y, x^y, x%y
    [root@centos6 app]# awk -v n=10 -v m=3 'BEGIN{print n%m}'
    1

    -x: 转换为负数
    +x: 转换为数值
    - 字符串操作符:没有符号的操作符,字符串连接
    - 赋值操作符

    =, +=, -=, *=, /=, %=, ^=
    ++, --
    [root@centos6 app]# awk -v n=10 -v m=3 'BEGIN{print m+=n}'
    13
    [root@centos6 app]# awk -v n=10 -v m=3 'BEGIN{print m+=n,m++,m}'  这个是m++是先读取在自+
    13 13 14 
    [root@centos6 app]# awk -v n=10 -v m=3 'BEGIN{print m+=n,++m,m}'
    13 14 14
    下面两语句有何不同
    awk 'BEGIN{i=0;print ++i,i}'
    [root@centos6 app]# awk 'BEGIN{i=0;print ++i,i}'
    1 1
    awk 'BEGIN{i=0;print i++,i}'
    [root@centos6 app]# awk 'BEGIN{i=0;print i++,i}'
    0 1
    • 比较操作符
    ==, !=, >, >=, <, <=
    • 模式匹配符
    ~:左边是否和右边匹配包含只要包含就匹配 !~:是否不匹配
    • 支持扩展的正则表达式,一般正则表达式都要放在两个斜线之前,或者双引号
    awk –F: '$0 ~ /root/{print $1}' /etc/passwd
    awk '$0~“^root"' /etc/passwd
    awk '$0 !~ /root/' /etc/passwd
    awk –F: '$3==0' /etc/passwd
    [root@centos6 app]# awk '$0~"^root"' /etc/passwd   awk如果不写花括号,模式是print $0打印整行
    root:x:0:0:root:/root:/bin/bash
    [root@centos6 app]# awk '$0 ~ /^bin/' /etc/passwd   正则表达式是要放在两个斜线之前的
    bin:x:1:1:bin:/bin:/sbin/nologin
    [root@centos6 app]# awk -F: '$3==0' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    [root@centos6 app]# awk -F: '$3>=500' /etc/passwd  这里相当于每读入一行就判断
    nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
    guo:x:500:500::/home/guo:/bin/bash
    [root@centos6 app]# 
    ============================
    [root@centos6 app]# awk  -v i= "i" /etc/issue   i没有赋值是假
    [root@centos6 app]# awk  -v i="" "i" /etc/issue 这样也是假
    [root@centos6 app]# awk  -v i=0 "i" /etc/issue   i等于0也是假
    [root@centos6 app]# awk  -v i=" " "i" /etc/issue  i等于空是真,打印全部内容
    CentOS release 6.9 (Final)
    Kernel \r on an \m
    只要不是0,空,或没有定义,都算真,负数也算真
    • 当pattern条件为真的时候执行{}花括号里的内容
    • 逻辑操作符:与&&,或||,非!
    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
    [root@centos6 app]# awk -F: -v i=10 'i' /etc/issue
    CentOS release 6.9 (Final)
    Kernel \r on an \m
    
    [root@centos6 app]# awk -F: -v i=10 '!i' /etc/issue
    [root@centos6 app]# awk -F: -v i=10 '{print i}' /etc/issue  
    10
    10
    10
    [root@centos6 app]# awk -F: -v i=10 '{print !i}' /etc/issue
    0
    0
    0
    假取反就是真
    [root@centos6 app]# awk '{i=0;print !i++,i}' /etc/issue 先取反打印,然后在运算
    1 1
    1 1
    1 1
    [root@centos6 app]# awk '{i=-1;print !i++,i}' /etc/issue 
    0 0
    0 0
    0 0
    
    [root@centos6 app]# awk '{i=0;print !++i,i}' /etc/issue     先运算++,得出的数值在取反   
    0 1
    0 1
    0 1
    [root@centos6 app]# awk '{i=-1;print !++i,i}' /etc/issue 
    1 0
    1 0
    1 0
    • 函数调用: function_name(argu1, argu2, …)
    • 条件表达式(三目表达式)
      • selector?if-true-expression:if-false-expression
      • 用?号,冒号隔成三段
      • 其中?号是条件判断,如果条件为真则执行?号后面的语句,如果为假则执行冒号后面的语句
    awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd
    [root@centos6 app]# awk -F: '{$3>=500?usertype="common user":usertype="sysuser" ;printf "%15s %30s %d \n", usertype,$1,$3 }' /etc/passwd
            sysuser                           root 0 
            sysuser                            bin 1 
            sysuser                         daemon 2 
            sysuser                            adm 3 
            sysuser                             lp 4 
            sysuser                           sync 5 
            sysuser                       shutdown 6 
            sysuser                           halt 7 
            sysuser                           mail 8 
            sysuser                           uucp 10 
            sysuser                       operator 11 
            sysuser                          games 12 
            sysuser                         gopher 13 
            sysuser                            ftp 14 
            sysuser                         nobody 99 
            sysuser                           dbus 81 
            sysuser                        usbmuxd 113 
            sysuser                            rpc 32 
            sysuser                          rtkit 499 
            sysuser                  avahi-autoipd 170 
            sysuser                           vcsa 69 
            sysuser                           abrt 173 
            sysuser                        rpcuser 29 
        common user                      nfsnobody 65534 
            sysuser                      haldaemon 68 

    awk PATTERN

    • PATTERN:根据pattern条件,过滤匹配的行,再做处理
      1. 如果未指定:空模式,匹配每一行,所有都匹配
      2. /regular expression/:仅处理能够模式匹配到的行,需要用/ /里面可以写正则表达式
        • awk ‘/^UUID/{print $1}’ /etc/fstab 也可以不用~直接写正则表达式
        • awk ‘!/^UUID/{print $1}’ /etc/fstab
      3. relational expression: 关系表达式,结果为“真”才会被处理
        • 真:结果为非0值,非空字符串
        • 假:结果为空字符串或0值
    awk -F: 'i=1;j=1{print i,j}' /etc/passwd
    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/passwd
    取奇数行和偶数行
    [root@centos6 app]# seq 10 | sed -n '1~2p'
    1
    3
    5
    7
    9
    [root@centos6 app]# seq 10 | sed -n '2~2p'
    2
    4
    6
    8
    10
    用awk来取奇数和偶数行
    [root@centos6 app]# seq 10 | awk 'i=!i'   因为i为假,然后对i取反就是真那么第一行就是真打印第一行,当读入第2行时i已经是真了,所以i取反为假就不打印第2行
    1
    3
    5
    7
    9
    [root@centos6 app]# seq 10 |awk -v i=11 'i=!i'
    2
    4
    6
    8
    10
    [root@centos6 app]# seq 10 |awk  '!(i=!i)'       
    2
    4
    6
    8
    10
    1. 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
    [root@centos6 app]# awk '/^f/,/^r/' /etc/passwd   首先找到第一pat1是开始显示,一直找到pat2结束
    ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
    nobody:x:99:99:Nobody:/:/sbin/nologin
    dbus:x:81:81:System message bus:/:/sbin/nologin
    usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
    rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
    sed如何显示从第几行到第几行
    [root@centos6 app]# seq 10 | sed -n '2,5p'
    2
    3
    4
    5
    awk如何显示从第几行到第几行
    [root@centos6 app]# seq 10 | awk 'NR>=2&&NR<=5'
    2
    3
    4
    5

    5.BEGIN/END模式

    BEGIN{}: 仅在开始处理文件中的文本之前执行一次
    END{}:仅在文本处理完成之后执行一次

    示例

    awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3}END{print "end file"}' /etc/passwd
    awk -F : '{print "USER USERID“;print $1":"$3} END{print"end file"}' /etc/passwd
    awk -F: 'BEGIN{print " USER UID \n---------------"}{print $1,$3}' /etc/passwd
    awk -F: 'BEGIN{print " USER UID \n---------------"}{print $1,$3}'END{print "=============="} /etc/passwd
    seq 10 |awk 'i=0'
    seq 10 |awk 'i=1'
    seq 10 | awk 'i=!i'
    seq 10 | awk '{i=!i;print i}'
    seq 10 | awk '!(i=!i)'
    seq 10 |awk -v i=1 'i=!i
    [root@centos6 app]# awk -F: 'BEGIN{printf "USERNAEM                        UDI\n=================================\n"}{printf "%-30s %d\n",$1,$3}' /etc/passwd    
    USERNAEM                        UDI
    =================================
    root                           0
    bin                            1
    daemon                         2
    adm                            3
    lp                             4
    sync                           5
    shutdown                       6
    halt                           7
    mail                           8
    uucp                           10
    operator                       1
    guo                            500
    [root@centos6 app]# awk -F: 'BEGIN{printf "=================================\nUSERNAEM                     UDI\n=================================\n"}{printf "%-30s %d\n",$1,$3}' /etc/passwd   
    =================================
    USERNAEM                     UDI
    =================================
    root                           0
    bin                            1
    daemon                         2
    adm                            3
    lp                             4
    sync                           5
    shutdown                       6
    halt                           7
    mail                           8
    uucp                           10
    operator                       11
    [root@centos6 app]# awk -F: 'BEGIN{printf "=================================\nUSERNAEM                    | UDI\n=================================\n"}{printf "|%-30s | %d\n=================================\n",$1,$3}' /etc/passwd
    =================================
    USERNAEM                    | UDI
    =================================
    |root                           | 0
    =================================
    |bin                            | 1
    =================================
    |daemon                         | 2
    =================================
    |adm                            | 3
    =================================
    |lp                             | 4
    =================================
    |sync                           | 5
    

    awk action

    • 常用的action分类
      1. Expressions:算术,比较表达式等
      2. Control statements:if, while等
      3. Compound statements:组合语句
      4. input statements
      5. output statements:print等

    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

    awk控制语句if-else

    语法
    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"}}'
    [root@centos6 app]# awk -F: '{if ($3>=500){usertype="common user" ;print usertype,$1,$3}}' /etc/passwd
    common user nfsnobody 65534
    common user guo 500
    取出分区利用率
    [root@centos6 app]# df | awk '/^\/dev\/sd/{print $1,$(NF-1)}'
    /dev/sda2 10%
    /dev/sda3 1%
    /dev/sda1 4%
    [root@centos6 app]# df | awk '{if($0~/^\/dev\/sd/)print $1,$(NF-1)}'
    /dev/sda2 10%
    /dev/sda3 1%
    /dev/sda1 4%
    取当分区利用率大于多少时取出对应的设备名
    [root@centos6 app]# df | awk -F% '/^\/dev\/sd/{print $(NF-1)}'|awk  '{if($NF>=10)print $1,$NF}'    
    /dev/sda2 10
    也可以使用用多个分隔符
    [root@centos6 app]# df | awk -F "[ %]+" '/^\/dev\/sd/{print $1,$5}'
    /dev/sda2 10
    /dev/sda3 1
    /dev/sda1 4

    awk控制语句while循环

    • while循环
    • 语法:while(condition){statement;…}
    • 条件“真”,进入循环;条件“假”,退出循环
    • 使用场景
      • 对一行内的多个字段逐一类似处理时使用,对字段进行循环
      • 对数组中的各元素逐一处理时使用
      • length($1)这是一个函数,统计某个字段有多长
    [root@centos6 app]# awk -F: '{print $1,length($1)}' /etc/passwd
    root 4
    bin 3
    daemon 6
    adm 3
    lp 2
    sync 4
    shutdown 8
    halt 4
    mail 4
    uucp 4
    operator 8
    games 5
    gopher 6
    ftp 3
    在shell中如何判断字符数
    [root@centos6 app]# v=hahah
    [root@centos6 app]# echo ${#v}
    5
    判断root这一行每个字段各是多少个字符
    [root@centos6 app]# awk -F: '/^root/{i=1;while(i<=NF){print $i ,length($i);i++}}' /etc/passwd
    root 4
    x 1
    0 1
    0 1
    root 4
    /root 5
    /bin/bash 9
    如何用awk语句从1加到100之和  
    [root@centos6 app]# awk 'BEGIN{i=1;sum=0;while(i<=100){sum+=i;i++}print sum}'
    5050
    [root@centos6 app]# awk 'BEGIN{i=1;sum=0;do{sum+=i;i++}while(i<=100)print sum}'
    5050
    [root@centos6 app]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++)sum+=i;print sum}' 
    5050
    用seq计算1100之和
    [root@centos6 app]# seq -s+ 1 100 |bc
    5050

    示例

    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)>=10){print $i,length($i)}; i++}}' /etc/grub2.cfg

    awk控制语句do-while循环

    • 语法:do {statement;…}while(condition)d
    • 意义:无论真假,至少执行一次循环体
      示例
     awk 'BEGIN{ total=0;i=0;do{total+=i;i++;}while(i<=100);print total}'

    awk控制语句for循环

    • 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

    性能比较

    • 测试中发现,awk的性能高于shell性能
    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)
    [root@centos6 app]# time awk 'BEGIN{sum=0;for(i=1;i<=10000000;i++)sum+=i;print sum}'
    50000005000000
    
    real    0m0.923s
    user    0m0.921s
    sys     0m0.002s
    这里时间第一个时间表示一共使用多长时间
    第2个是用户空间使用时间
    第3个系统使用空间
    

    awk控制语句switch语句

    • switch语句
    • 语法
    switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2;...; default: statementn}

    awk控制语句break和continue

    • 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 1,$3}’ /etc/passwd
    [root@centos6 app]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2!=0)continue ;sum+=i}print sum}'  
    2550
    [root@centos6 app]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)break;sum+=i}print sum}'
    1225
    只对奇数行进行处理
    [root@centos6 app]# awk -F: '{if($3%2!=1)next;print $1,$3}' /etc/passwd  
    bin 1
    adm 3
    sync 5
    halt 7
    operator 11
    gopher 13
    nobody 99

    awk数组

    • 关联数组:array[index-expression]
    • index-expression
      1. 可使用任意字符串;字符串要使用双引号括起来
      2. 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为”空串”
      3. 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历
        示例
    weekdays[“mon”]="Monday"
    awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
    awk '!arr[$0]++' dupfile
    awk '{!arr[$0]++;print $0, arr[$0]}' dupfile
    用awk把文件中重复的行去掉
    [root@centos6 app]# cat aaa 
    aaaa
    bbbb
    cccc
    aaaaaa
    aaaa
    bbbb
    ddddd
    aa
    ddddd
    [root@centos6 app]# awk '!arr[$0]++' aaa 
    aaaa
    bbbb
    cccc
    aaaaaa
    ddddd
    shell去重复行方法
    [root@centos6 app]# sort aaa | uniq
    aa
    aaaa
    aaaaaa
    bbbb
    cccc
    ddddd
    • awk ‘!arr[$0]++’ aaa
      • 因为arr是一个数组,数组是多个变量的集合,要想在数组中标识一个变量,就要用数组名加下标(索引编号),我们这里用文件的行来定义下标,arr是数组[$0],然后先取反因为这个数组的变量是没有赋值所以默认是假,但是取反就是真,如果是真就++,0++就是1 这里会赋值为1打印此行,当有一样符下标时因为上面已经赋值为1了,取反就为0,0是假不打印此行,然后在++
    • 若要遍历数组中的每个元素,要使用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
    [root@centos6 app]# netstat -nta | awk '/^tcp/{state[$NF]++}END{for(i in state){print i,state[i]}}' 
    ESTABLISHED 1
    LISTEN 11
    查找日志中的ip链接数和对应ip
    [root@centos6 app]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log 
    172.18.56.3 159091
    192.168.27.6 4004
    172.18.0.100 24
    分析日志中链接数大于1000次的ip并加入到IPtables中
    [root@centos6 app]# awk '{ip[$1]++}END{for(i in ip)if(ip[i]>1000){print i}}' access_log |while read l ;do iptables -A INPUT -s $l -j REJECT; done
    分析会话链接数前十个的ip
    [root@centos6 app]# ss -nt|awk '/^ESTAB/{print $NF}'|awk -F: '{print $1}'|sort|uniq -c | sort -nr | head -n3
    分析会话链接数前十个的ip并加入iptables中
    [root@centos6 app]# ss -nt|awk '/^ESTAB/{print $NF}'|awk -F: '{print $1}'|sort|uniq -c | sort -nr | head -n3 | while read coo ip;do iptables -A INPUT -s $ip -j REJECT;done
    • 数值处理
      • rand():返回0和1之间一个随机数
    awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
    [root@centos6 app]# awk 'BEGIN{print rand()}'  //如果直接这样写会固定值不变,要想生成随机数需要调用另一个函数srand()
    0.237788
    [root@centos6 app]# awk 'BEGIN{print rand()}'
    0.237788
    [root@centos6 app]# awk 'BEGIN{print rand()}'
    0.237788
    [root@centos6 app]# awk 'BEGIN{srand();print rand()}' 这样就生成随机数,
    0.434576
    [root@centos6 app]# awk 'BEGIN{srand();print rand()}'
    0.510123
    [root@centos6 app]# awk 'BEGIN{srand();print rand()*10}' 我们可以成*10或更大
    3.07812
    [root@centos6 app]# awk 'BEGIN{srand();print int(rand()*10)}'加上int就是取整数
    8
    [root@centos6 app]# awk 'BEGIN{srand();print int(rand()*100)}'
    77
    [root@centos6 app]# awk 'BEGIN{srand();print int(rand()*100)}'
    17
    • 字符串处理
      • length([s]):返回指定字符串的长度
      • sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为s
    echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
    [root@centos6 app]# echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'   sub默认只替代第一个匹配的
    2008-08:08 08:08:08
    [root@centos6 app]# echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$1)'   gsub会把整个字段都匹配,因为默认是以空白符分隔所以2008:08:08是第一个字段
    2008-08-08 08:08:08
    [root@centos6 app]# echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'  可以用$0整行匹配
    2008-08-08 08-08-08
    - 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]}}'
    [root@centos6 app]# echo "2008:01:02 03:04:05" | awk '{split($0,str,":")}END{for(i in str){print i,str[i]}}'  这是以冒号为分隔符,生成一个数组str,是整行分割,str数组下标1的值是2008
    4 04
    5 05
    1 2008
    2 01
    3 02 03
    统计文件中每个单词出现的次数
    [root@1234 ~]# awk '{for(i=1;i<=NF;i++)word[$i]++}END{for(i in word)print word[i],i}' /etc/profile | sort -nr
    13 #
    9 [
    8 then
    8 if
    如何用awk显示男生,女的总成绩,和平均值
    [root@1234 ~]# vim f1
    li   85  female
    guo 100 male
    zhang  90 male
    wang 99 female
    li   85  female
    [root@1234 ~]# awk '{if($3=="male"){mnum++;msun+=$2}else{fnun++;fsum+=$2}}END{printf "male:%d  %5.2f\nfemale:%d %5.2f\n",mnum,msun/mnum,fnun,fsum/fnun}' f1
    male:2  95.00
    female:2 92.00
    用数组的方法
    [root@1234 ~]# awk '{num[$3]++;sum[$3]+=$2}END{for(sex in num)print sex,num[sex],sum[sex]/num[sex]}' f1   
    female 2 92
    male 2 95

    awk函数

    • 自定义函数
      格式
    function name ( parameter, parameter, ... ) {
                statements
                return expression
    }
    ( parameter, parameter, ... ) 这里面的表示是参数,位置参数

    示例

    cat fun.awk
    function max(v1,v2) {
            v1>v2?var=v1:var=v2
            return var
    }
    BEGIN{a=3;b=2;print max(a,b)}
    awk –f fun.awk
    这个函数表示max(v1,v2)是形式上的参数,是没有任何赋值的,只是代表着位置参数,而BEGIN{a=3;b=2;print max(a,b)}a,b是实际的参数,形式的参数和实际的参数可以名字不一样,但是数量上一定要相等。
    v1>v2?var=v1:var=v2  这是是要执行的程序
    return var :return这个必须要写,要返回一个值
    文件里的不用加单引号
    awk 用-f 调用这个函数
    [root@1234 ~]# vim fun.awk    
    function max(v1,v2) {
           v1>v2?var=v1:var=v2
            return var
    }
    BEGIN {a=3;b=2;print max(a,b)}
    [root@1234 ~]# awk -f fun.awk 
    3
    
    • 也可以不写a=3;b=2;,不在文件中定义死,我们可以用-v来定义变量
    [root@1234 ~]# vim fun.awk 
    function max(v1,v2) {
           v1>v2?var=v1:var=v2
            return var
    }
    BEGIN {print max(a,b)}
    [root@1234 ~]# awk -v a=20 -v b=30 -f fun.awk 
    30                    

    awk中调用shell命令

    • system命令
    • 空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用”“引用起来
    awk BEGIN'{system("hostname") }'
    awk 'BEGIN{score=100; system("echo your score is " score) }'
    [root@1234 ~]# awk 'BEGIN{system("hostname")}'
    1234
    • 这里要注意一点的是用echo显示变量时要注意几点
      1. 因为这里定义的变量i是awk中定义的变量,如果想要用echo显示出来需要放在引号外面
      2. echo后面一定要有空格,否则会保语法错误
    [root@1234 ~]# awk 'BEGIN{i=10;system("echo i ")}'
    i
    [root@1234 ~]# awk 'BEGIN{i=10;system("echo"i)}'   这里的echo没有空格所以报语法错误   
    sh: echo10: command not found
    [root@1234 ~]# awk 'BEGIN{i=10;system("echo "i)}'
    10

    awk脚本

    • 将awk程序写成脚本,直接调用或执行
    [root@1234 ~]# vim fun.awk 
    #!/bin/awk -f      
    function max(v1,v2) {
           v1>v2?var=v1:var=v2
            return var
    }
    BEGIN {print max(a,b)}
    [root@1234 ~]# chmod +x fun.awk 
    [root@1234 ~]# ./fun.awk -v a=10 -v b=20
    20

    示例

    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

    向awk脚本传递参数


    • 格式:
      • 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/passwd

    你可能感兴趣的:(linux,AWK)