awk

文本处理工具awk

1. 简介

  • 来由与作用
    awk是由Aho, Weinberger, Kernighan三位大牛开发的程序,用于报告生成器,格式化文本输出,可接收标准输入
  • 版本

  1. New awk(nawk)
  2. GNU awk( gawk),centos使用的为gawk
  • 运行原理

    1. 分割符、域和记录

    1. awk执行时,由分隔符分隔的字段(域)标记1,1,0为所有域,注意:和shell中变量$符含义不同
    2. 文件读入内存空间的每一行称为记录,文本中的一行可以为多个记录
    3. 省略action,则默认执行 print $0 的操作
  • 工作原理

    1. 执行BEGIN{action;… }语句块中的语句
    2. 从文件或标准输入(stdin)读取一个记录到内存空间,执行pattern{ action;… }语句块,匹配预定义的模式,如果不匹配就不处理,匹配后按照预先规定好的分隔符切割成若干字段,分割成功后,系统会对这些列分配一个域标识$1..$n,直到文件全部被读取完毕。
    3. 当读至输入流末尾时,执行END{action;…}语句块

    1. BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
    2. END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
    3. pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块

    2. 基本用法

    • 格式:
      awk [options] var=value ‘program’ file…
    • 选项(options):

    1. -v var=value: 自定义变量
    2. -f programfile: 读取文件中的程序
      awk [options] var=value -f programfile file…
    3. -F:指明输入时用到的字段分隔符,分隔符可以指定多个

    示例:指定[;]为记录分隔符,下例为2个记录a:b@c:d与1:2@3
    1:指定[:]为字段分割符
    $echo "a:b@c:d;1:2@3"|awk -v RS=";" -F":" '{print $1,$3}' 
        a d
        1 
    2:指定[@:]为字段分割符
    $echo "a:b@c:d;1:2@3"|awk -v RS=";" -F"[@:]" '{print $1,$3}' 
        a c     两个分隔符都起作用,c为第3个字段
        1 3
    3:以扩展正则表达式方法表示也可以
    $echo "a:b@c:d;1:2@3"|awk -v RS=";" -F"@|:" '{print $1,$3}'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • program
    为awk自己的语法,通常是被单引号或双引号中,建议使用单引号,因为双引号在程序中会在字符串上用,避免混用

    1. 格式:
      1. BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成
        ‘BEGIN{ action;… } pattern{ action;… } END{ action;… }’
      2. 常用格式:
        pattern{action statements;..}
    2. 释义pattern和action:
      1. pattern部分决定动作语句何时触发及触发事件
      2. action statements对数据进行处理,放在{ }内指明
        1. action格式: {print|printf item1, item2, …}
        2. 要点:
          1. 逗号分隔符
          2. 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式
          3. 如省略item,相当于print $0
  • 示例
    例如:打印出/etc/passwd文件的第1和第3行

  •  awk -F: '{print $1,$3}' /etc/passwd
    • 1

    3. awk变量

    变量:分为内置和自定义变量,只要对变量赋值,就要放在选项-v的后面

    3.1 内置变量

    • FS(field separator):输入字段分隔符,默认为空白字符,指定变量的好处在于,可以多次调用

    1. 示例1:定义FS=’:’,在(程序)中调用变量

      $awk -v FS=':' '{print $1,FS,$3}’ /etc/passwd
      named:25
    2. 示例2:一次引用多个变量,中间需要使用逗号隔开

      $awk -v FS=: '{print $1FS,FS,FS$3}' /etc/passwd
      named: : :25
    3. 示例3:还可以引用shell中的变量,先声明fs=:,然后用awk调用

      $fs=:;awk -v FS="$fs" '{print $1FS$3}' /etc/passwd
      named:25
  • OFS(output field separator):指定输出字段分隔符,默认为空白字符

    1. 示例:以###作为字段分隔符

      $awk -F":" -v OFS=### '{print $1,$3,$2}' /etc/passwd
      named###25###x
  • RS(record separator):输入记录分隔符,指定输入时的换行符

    1. 示例:创建文本,如下awk.txt所示,[@,!,?]与[1,2,3,4]以及[A,B,C aa,bb,cc],以[;]分隔开

    1. 创建实验文本    
    $cat awk.txt
    @,!,?;1,2,3,4;A,B,C
    aa,bb,cc
    
    2. 指定输入字段分隔符为[,],输出字段分隔符为[==],输入记录分隔符为[;]
    $awk -v FS="," -v OFS="==" -v RS=";" '{print $3,$4}' awk.txt 
    ?==                <==第一个记录@,!,?没有第4个字段
    3==4               <==第二个记录1,2,3,4,显示后两个
    C                  <==第三个记录,保留原有格式换行,C
    aa为第三个字段
    aa==bb
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • ORS:输出记录分隔符,输出时用指定符号代替换行符

    1. 承上例,每条记录输出时默认为换行处理,可以通过ORS指定

    $awk -v FS="," -v OFS="==" -v RS=";" -v ORS="***" '{print $3,$4}' awk.txt
    ?==***3==4***C   <==记录输出时以指定的符号代替原回车换行
    aa==bb***
    
    • 1
    • 2
    • 3
  • NF:字段数量

  • $awk -F: '{print NF}' /etc/passwd    
    7    <==统计字段数量
    $awk -F: '{print $(NF-6)}' /etc/passwd
    named     <==显示第一列内容,相当于$1,要用括号括起来
    
    • 1
    • 2
    • 3
    • 4
  • NR:记录号

  • $awk -F: '{print NR,$1}' /etc/passwd
    45 named     <==相当于多了个行号
    
    • 1
    • 2
  • FNR:各文件分别计数,记录号

  • $awk -F: '{print NR,$1}' /etc/passwd  /etc/group
    121 named        <==一共有多少记录
    
    处理多个文件时,使用NR会将记录号累计,不会按文件分开,这时需要使用FNR
    $awk -F: '{print FNR,$1}' /etc/passwd  /etc/group
    45 named        <==/etc/passwd文件最后一个记录
    76 named         <==/etc/group文件最后一个记录
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • FILENAME:当前文件名
    承上例,如果觉得显示不清楚,可以加上文件名

  • $awk -F: '{print FNR,FILENAME,$1}' /etc/passwd  /etc/group
    76 /etc/group named
    
    • 1
    • 2
  • ARGC:命令行参数的个数

  • $awk  '{print ARGC}' /etc/passwd  /etc/group
    3    <==统计命令行参数的个数
    
    • 1
    • 2
  • ARGV:数组,保存的是命令行所给定的各参数
    上例为什么是3个参数?使用ARGV查看各参数

  • $awk  '{print ARGV[0] }' /etc/passwd  /etc/group
    awk     <==将awk算为了一个参数
    $awk  '{print ARGV[2] }' /etc/passwd  /etc/group
    /etc/group   <==第三个参数
    
    • 1
    • 2
    • 3
    • 4

    3.2 自定义变量(区分字符大小写)

    • 格式
      1. -v var=value 例如 -v NAME=USERNAME
      2. 直接在程序中定义变量{NAME=USERNAME;print…}
    • 示例

    1. 示例:使用格式1自定义

    $awk -F: -v USER=“username” -v UID=“userid” ‘{print USER":"$1,UID":"$3}’ /etc/passwd  /etc/group
    username:named userid:25 这样显示不是很清晰,还记得记录分隔符吗
    $awk -F: -v USER=“username” -v UID=“userid” -v ORS=" ------ " ‘{print USER":"$1" "UID":"$3}’ /etc/passwd /etc/group

    username:screen
    userid:84

    username:named
    userid:25

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

  • 示例:使用格式2自定义

  • $awk -F: -v ORS="
    ------
    " '{USER="username"; UID="userid"; print USER":"$1"
    "UID":"$3}' /etc/passwd  /etc/group
    
    • 1
  • 示例:将action放入文件,使用-f选项

  • 1. 将程序内容放入文件awk2.txt 中
    $cat awk2.txt 
    {USER="username"; UID="userid"; print USER":"$1"
    "UID":"$3}
    2. 执行以下命令,写入文件的程序就不需要单引号了   
    $awk -F: -v ORS="
    ------
    " -f awk2.txt  /etc/passwd
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4. action中的printf命令

    • 格式
      printf “FORMAT”, item1, item2, …
    • 注意事项

    • 必须指定FORMAT
    • 不会自动换行,需要显式给出换行控制符,
    • FORMAT中需要分别为后面每个item指定格式符
  • 格式符:与item对应

  • 符号 释义
    %c 显示字符的ASCII码
    %d, %i 显示十进制整数
    %c 显示字符的ASCII码
    %e, %E 显示科学计数法数值
    %g, %G 以科学计数法或浮点形式显示数值
    %s 显示字符串
    %u 无符号整数
    %% 显示%自身
    • 修饰符

    • #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,如%3.1f
    • -: 左对齐(默认右对齐),如%-15s
    • +:显示数值的正负符号,如%+d
  • 示例

  • 示例1: 取出/etc/passwd第1,3行,并在其前分别加Username和UID

    awk -F: ‘{printf "Username: %-15s,UID:%d
    ",$1,$3}’ /etc/passwd
    • 1

    示例2:%s对应$1,表示文件第一行为字符串,在%与s中间可以添加参数[-]表示左对齐[20]表示第一列所占宽度,同理%-4d表示$2占4个字符宽度左对齐

    $awk -F: '{printf"|%-20s %-4d|
    ---------------------------
    ",$1,$3 }' /etc/passwd
    |named                25  |
    ---------------------------
    • 1
    • 2
    • 3

    示例3:在这里演示一下BEGIN的效果,添加表头,效果如下

    $awk -F: 'BEGIN{print "|user                 |ID  |
    ----------------------------"}''{printf"|%-20s |%-4d|
    ----------------------------
    ",$1,$3 }' /etc/passwd 
    |user                 |ID  |        <==头部,BEGIN所述内容
    ----------------------------
    |root                 |0   |        <==文件内容,printf所述内容
    ----------------------------
    • 1
    • 2
    • 3
    • 4
    • 5

    示例4:小数处理示例

    1$echo "3.1415 1.15926" |awk '{printf "%2.2f;%1.4f",$1,$2}'
        3.14;1.1593
        %2.2f第一个2表示宽度,第二个2个小数点保留位数
        %1.4f最后一位达到6时会进位
    2$echo "3.1415 1.15925" |awk '{printf "%2.2f;%1.4f",$1,$2}'
        3.14;1.1592
        %1.4f最后一位值为5时不会进位
    3$echo "3.1415 11.15925" |awk '{printf "%2.2f;%10.4f",$1,$2}'
        3.14;***11.1593    <==这里为了显示效果,以***代替空格,共10个宽度
        %10.4f宽度起作用了,说明宽度如果小于实际占用值,会以实际宽度为准
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5. 操作符

    5.1 算术操作符:

    操作符 定义
    +
    -
    *
    /
    % 取余
    ^
    -x 转换为负数
    +x 转换为数值

    5.2 赋值操作符:

    操作符 定义
    = 赋值
    += 将加的结果赋给变量
    -= 将减的结果赋给变量
    *= 将乘的结果赋给变量
    /= 将除的结果赋给变量
    %= 将取余的结果赋给变量
    ^= 将取幂的结果赋给变量
    ++ 变量加1
    变量减1

    • 示例:下面两语句有何不同
      awk ‘BEGIN{i=0;print ++i,i}’
      awk ‘BEGIN{i=0;print i++,i}’
    [root@hai7 ~]$awk 'BEGIN{i=10;print ++i,i}'
    11 11
    先递增,再将递增值打印
    [root@hai7 ~]$awk 'BEGIN{i=10;print i++,i}'
    10 11
    先打印i值,再递增
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.4 比较操作符:

    操作符 定义
    == 等于
    != 不等于
    > 大于
    >= 大于等于
    < 小于
    <= 小于等于

    5.5 模式匹配符

    操作符 定义
    ~ 左边是否和右边匹配
    !~ 是否不匹配

    • 示例
    1:筛选文件/etc/passwd中包含root的行
    [root@hai7 ~]$awk -F:  '$0 ~ /root/{print NR$1}'  /etc/passwd
    1 root
    10 operator
    
    2:筛选文件/etc/passwd中以root开头的行
    [root@hai7 ~]$awk -F:  '$0 ~ "^root"{print NR,$1}'  /etc/passwd
    1 root
    
    3:筛选文件/etc/passwd中不包含root的行
    [root@hai7 ~]$awk -F:  '$0 !~ /root/{print NR,$1}'  /etc/passwd
    2 bin
    9 mail
    11 games
    
    4:筛选第三字段为0的行
    [root@hai7 ~]$awk -F:  '$3==0'  /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    5.6 逻辑操作符:与&&,或||,非!

    操作符 定义
    && 逻辑与
    逻辑非

    • 示例
    1:筛选第3个字段大于等于50并且小于等于60的行
    [root@hai7 ~]$awk -F: '$3>=50 && $3<=60 {print NR,$1}' /etc/passwd 
    31 tss
    2:筛选第3个字段等于0或大于等于1000的行
    [root@hai7 ~]$awk -F: '$3==0 || $3>=1000 {print NR,$1}' /etc/passwd 
    1 root
    29 nfsnobody
    43 dong
    3:筛选第3个字段小于等于1000以外的行
    $awk -F: '!($3<=1000) {print NR,$1}' /etc/passwd 
    29 nfsnobody
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 条件表达式(三目表达式):
      (selector1)?if-true(expression2):if-false(expression)
      先求表达式selector1的值,如果为真,则执行表达式2,并返回表达式2的结果;如果表达式1的值为假,则执行表达式3,并返回表达式3的结果。

    • 示例:
    先定义条件第3字段大于等于1000,如果成立则打印type="user",不成立打印type="sys"
    [root@hai7 ~]$awk -F: '{$3>=1000?type="user":type="sys";printf"%+15s %s
    " ,$1,type}' /etc/passwd
    root sys
    nfsnobody user
    • 1
    • 2
    • 3
    • 4

    6 awk-PATTERN

    • PATTERN:根据pattern条件,过滤匹配的行,再做处理

    1. 如果未指定:空模式,匹配每一行
    2. /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来

    例1:判断磁盘利用率,取出设备行
    $df|awk -F " +|%" '/^/dev/sd/{print $1,$5}'
    /dev/sda2 11
    /dev/sda3 1
    /dev/sda1 16
    
    例2:统计磁盘文件系统类型
    $awk -F" +" '/^UUID/{print $3}' /etc/fstab|sort|uniq -c
    2 swap
    3 xfs
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • relational expression: 关系表达式,结果为“真”才会被处理

    • 真:结果为非0值,非空字符串
    • 假:结果为空字符串或0值
    • 示例

    1. 空格或者非0数值,返回值为非空,结果都为真,可以打印结果
      awk -F: '" "{print $1}' /etc/passwd
      awk -F: '1{print $1}' /etc/passwd
      awk ‘!0’ /etc/passwd
    2. 空值或者0,返回值为空,结果为假,不做处理
      awk -F: ' ""{print $1} ' /etc/passwd
      awk -F: '0{print $1}' /etc/passwd
      awk ‘!12345’ /etc/passwd 任何非0值取反都为空
    3. 下例分为两条命令

    [root@hai7 ~]$awk -F: 'i=1;j=1{print i,j}' /etc/passwd
    named:x:25:25:Named:/var/named:/sbin/nologin
            1 1
    i=1;j=1{print i,j}是两条命令
    i=1省略了{print ...}所以默认打印$0
    seq 10 |awk ‘i=0’
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 取偶数行或奇数行
    打印奇数行,因为i没有指定值所以为空,第一次执行时为空=!空,所以打印1,第二次执行时,非空=!非空值为0,所以不处理,以此类推,可以通过seq 10 | awk '{i=!i;print i}'查看返回结果
    • seq 10 |awk 'i=1'打印所有数字
    • seq 10 | awk 'i=!i'打印奇数行
    • seq 10 | awk '!(i=!i)'打印偶数行
    • seq 10 |awk -v i=1 'i=!i'打印偶数行
  • line ranges:行范围

    • 利用正则表达式写法取范围
      startline,endline:/pat1/,/pat2/ 不支持直接给出数字格式
      awk -F: ‘/^root>/,/^nobody>/{print $1}' /etc/passwd

    • 先要处理指定的行范围,可以利用NR记录编号来实现
      awk -F: '(NR>=10&&NR<=20){print NR,$1}' /etc/passwd

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

    • 示例

    示例1:
    [root@hai7 ~]$awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "end file"}' /etc/passwd
    USER USERID     <==BEGIN的内容
    root:0
    apache:48
    named:25
    end file        <==END的内容
    示例2:
    $awk -F: '{print "USER USERID";print $1":"$3} END{print "end file"}' /etc/passwd
    USER USERI     <==不是开始处理的文本,会被循环打印
    root:0
    USER USERI
    bin:1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    7. awk action

    7.1awk控制语句if-else

    • 语法
      if(condition){statement;…}[else statement]
      if(condition1){statement1}else if(condition2){statement2}else{statement3}
    • 使用场景:对awk取得的整行或某个字段做条件判断
    • 示例:

    示例1:取出第3字段大于等于1000的行

    awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
    awk -F: '$3>=1000{print $1,$3}' /etc/passwd
    效果相同,格式不同控制语句是action里的要写在花括号内,判断语句是属于patter的要写在花括号外面
    • 1
    • 2
    • 3

    示例2:取磁盘利用率高于5%的

    $df|awk -F" +|%" '/^/dev/sd/{if($5>5){print $1,$5;print "it is full"}}'
    /dev/sda2 11
    it is full
    /dev/sda1 16
    it is full
    • 1
    • 2
    • 3
    • 4
    • 5

    示例3:取访问日志中IP地址重复次数超过100的,放到防火墙中

    [root@hai7 ~]$ab -c10 -n 100 http://172.20.50.201/     实验准备多次访问某地址
    [root@hai7 ~]$cat /var/log/httpd/access_log|awk -F" " '{print $1}'|uniq -c|awk -F" " '{if($1>100){print $2}'|while read ip;do iptables -A INPUT -s $ip -j REJECT;done
    [root@hai7 ~]$iptables -F  清空防火墙
    • 1
    • 2
    • 3

    示例3:shell类型使用bash的行

    awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
    • 1

    示例4:第3字段大于等于1000的标记为Common user,否则标记为root or Sysuser

    awk -F: '{if($3>=1000) {printf "Common user: %s
    ",$1} else {printf "root or Sysuser: %s
    ",$1}}' /etc/passwd
    awk -F: '{if($3>=1000) printf "Common user: %s
    ",$1; else printf "root or Sysuser: %s
    ",$1}' /etc/passwd
    • 1
    • 2

    7.2 awk控制语句 while循环

    • 语法:
      while(condition){statement;…}
    • 条件“真”,进入循环;条件“假”,退出循环
    • 使用场景:
      对一行内的多个字段逐一类似处理时使用
      对数组中的各元素逐一处理时使用
    • 示例:

    示例1:用awk写1..100相加

    $awk 'BEGIN{sum=0;i=1;while(i<=100){sum+=i;i++};print sum}'
    5050
    • 1
    • 2

    示例2:找出不管前面多少空格linux16开头的行,统计各字段字符串长度

    [root@hai7 ~]awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i); i++}}' /etc/grub2.cfg
    
    示例中length是系统函数,用来统计括号中字符串长
    [root@hai7 ~]$awk 'BEGIN{print length("sdgdgasdfwergejut")}'
    17
    • 1
    • 2
    • 3
    • 4
    • 5

    示例3:统计字段字符串长度超过10的

    awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=10) {print $i,length($i)}; i++}}' /etc/grub2.cfg 
    • 1

    示例4:判断随机数最大值与最小值

    1:生成以逗号为分隔符的随机数到文件f1
    [root@hai7 ~]$(echo "$RANDOM" ;for((i=1;i<100;i++));do echo -n ",$RANDOM";done)>f1
    2: 取出随机数最大值和最小值
    [root@hai7 ~]$awk -F',' '{max=$1;min=$1;i=2;while(i<=NF){if($i>max){max=$i}else if($i<min){min=$i};i++;}}END{print "max="max,"min="min}' f1
    max=32377 min=148
    3:判断结果是否正确
    [root@hai7 ~]tr ','  'n' <f1.txt|shot  -nr
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    7.3 控制语句 do-while循环

    • 语法
      do {statement;…}while(condition)
    • 意义
      无论真假,至少执行一次循环体
    • 示例:

    示例1:1..100相加

    $awk 'BEGIN{sum=0;i=1;do {sum+=i;i++}while(i<=100);print sum}
    • 1

    示例2:统计linux16行,大于10个字符的字段

    $awk '/^[[:space:]]*linux16/{i=1;do {if(length($i)>=10){print $i,length($i)}; i++}while(i<=NF)}' /etc/grub2.cfg
    • 1

    7.4 awk控制语句 for循环

    • 语法
      for(expr1;expr2;expr3) {statement;…}
    • 常见用法:
      for(variable assignment;condition;iteration process){for-body}
    • 特殊用法:能够遍历数组中的元素
      - 语法:for(var in array) {for-body}
    • 示例:

    示例1:统计linux16行,各字段字符长度,

    awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
    • 1

    示例2:从1+到100相加

    awk 'BEGIN{sum=0;for(i=1;i<=100;i++)sum+=i;print sum}'
    • 1

    7.5 awk控制语句 switch语句

    • 语法:
      swich定义的表达式,是否为case后的只或者正则表达式,如果是则执行statement1, 如果不匹配则继续判断第二个case后的值是否匹配,匹配则执行statement2,都不匹配则执行default后续。

    • 格式
      switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; …; default: statementn}

    7.6 awk控制语句 break、continue及next

    • break [n] 结束整个循环体

        [root@hai7 /etc/selinux]$awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)break;sum+=i} ;print sum}'          
        1225
        循环到50,就不往后加了
    
    • 1
    • 2
    • 3
  • continue [n]结束单次循环
  • 1:i%2==1意思为奇数循环,跳过奇数循环,得偶数总和
    [root@hai7 /etc/selinux]$awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==1)continue;sum+=i} ;print sum}'
    2550
    2:结束第50次循环,会继续进行后续循环
    [root@hai7 /etc/selinux]$awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)continue;sum+=i} ;print sum}'
    5000
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • next:
      提前结束对本行处理而直接进入下一行处理(awk自身循环)
    打印  奇数行
    awk -F: '{if($3%2!=0)next;print NR,$1,$3}' /etc/passwd
    • 1
    • 2

    8. 性能比较

    使用awk、shell、bc计算100000相加,参考所用时间,判断工作性能
    1、awk for循环

    [root@hai7 /etc/selinux]$time awk 'BEGIN{sum=0;for(i=1;i<=100000;i++)sum+=i ;print sum}'
    5000050000
    
    real    0m0.011s
    user    0m0.010s
    sys 0m0.001s
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、awk while循环

    2. $time awk 'BEGIN{sum=0;i=1;while(i<=100){sum+=i;i++};print sum}'
    5050
    
    real    0m0.003s
    user    0m0.002s
    sys 0m0.001s
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、shell for循环

    $time for((sum=0,i=1;i<=100000;i++));do let sum+=i;done;echo $sum
    
    real    0m0.666s
    user    0m0.666s
    sys 0m0.000s
    • 1
    • 2
    • 3
    • 4
    • 5

    4、shell while循环

    
    $time ( sum=0;i=1;while [ $i -le 100000 ] ;do let sum+=i;let i++;done;echo $sum)
    5000050000
    
    real    0m1.064s
    user    0m0.992s
    sys 0m0.072s
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5、bc计算

    $time seq -s + 100000|bc
    5000050000
    
    real    0m0.041s
    user    0m0.036s
    sys 0m0.007s
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    9. awk数组

    9.1 awk支持关联数组

    arr[“key”]=value 键值对,每个键后面对应着相应的值, 当按下相应的键时, 就会输出相应的结果

    • 格式
      array[index-expression]
    • index-expression:
      • 可使用任意字符串;字符串要使用双引号括起来
      • 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
      • 若要遍历数组中的每个元素,要使用for循环,格式为for(var in array) {for-body}
    • 示例

    •awk ‘!arr[0]++dupfile0]++′dupfile去掉重复行,例如文件内容为cat f2
    abc
    cvb
    abc
    当awk读入第一行时,arr的下标为abc,没有赋值为空,取反为非空,所以打印第一行,arr[abc]=1
    读入第二行是,arr的下标为cbv,没有赋值为空,取反为非空,所以打印
    读入第三行时,arr的下标为abc,值为1,取反为空,所以不打印 arr[abc]=2

    去掉重复行还可以使用sort -u f2
    •awk ‘{!arr[0]++;print0]++;print0, arr[$0]}’ dupfile

    分别定义数组weekdays的键值对为[“mon”]=”Monday”,[“tue”]=”Tuesday”,通过for来遍历

    awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
    • 1

    统计tcp状态,NF代表最后一个字段,i表示值,state[i]表示累计结果

    netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state) { print i,state[i]}}'
    • 1

    统计IP访问次数,f1为导出的IP访问日志格式为,默认以空格为分隔符
    172.20.7.53 - - [10/Sep/2018:10:18:53 +0800] “GET / HTTP/1.0” 403 4961 “-” “ApacheBench/2.3”

    awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' f1|sort -rn
    • 1

    分别统计男生女生的平

    [root@hai7 /data]$cat f2
    name   grade  boyorgirls
    A   76  G
    B   98  M
    C   66  M
    D   95  G
    E   86  G       
    F   79  M
    [root@hai7 /data]$awk '!/^name/{if($3=="M"){score_m+=$2;number_m++}else{score_g+=$2;number_g++}}END{print "avg_m="score_m/number_m"","avg_g="score_g/number_g}' f2
    avg_m=81 avg_g=85.6667
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方法二

    [root@hai7 /data]$awk 'NR!=1{score[$3]+=$2;num[$3]++}END{for(sex in num){print sex":avg="score[sex]/num[sex]}}' f2
    G:avg=85.6667
    M:avg=81
    • 1
    • 2
    • 3

    9.2 自定义函数

    • 格式:
      function name ( parameter, parameter, … ) {
      statements
      return expression
      }
    • 示例:
    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
    3
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    9.3 awk中调用shell命令

    空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用”“引用起来。

    • 格式 system(“$var”)
    • 示例
      awk BEGIN'{system("hostname") }'
      awk 'BEGIN{score=100; system("echo your score is " score) }'

    9.4 awk脚本

    将awk程序写成脚本,直接调用或执行

    • 示例
    1:将pattern{action statements;..}写到文本中
    [root@hai7 /data]$cat f3
    {if($3>=1000)print $1,$3}
    2:调用文本来筛选文件
    $awk -F: -f f3 /etc/passwd
    nfsnobody 65534
    v9 1000
    
    3:建立awk执行脚本
    $cat f3
    #!/bin/awk -f    <==脚本解释器与shell不同
    #this is a awk script
    {if($3>=1000)print $1,$3}  <==pattern{action statements;..}
    4:加执行权限
    chmod +x f3     
    5:调用脚本来过滤文本
    ./f3 –F: /etc/passwd
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    9.4.1 向awk脚本传递参数

    • 格式
      awkfile var=value var2=value2… Inputfile
    • 注意事项
      • 在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。
      • 可以通过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数
    • 示例
    1:编辑awk脚本
    cat test.awk
    #!/bin/awk –f
    {if($3 >=min && $3<=max)print $1,$3}
    2:加执行权限
    chmod +x test.awk
    3:执行脚本,向脚本传递变量如下,也可以使用-v min=100,每个变量前都要加-v
    ./test.awk -F: min=100 max=200 /etc/passwd   
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    10. awk数值处理

    • rand():
      返回0和1之间一个随机数,需要配合srand实现,srand是用来生成随机数的种子
      awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
    • int 取整数
      例:生成随机1-1之间的小数awk 'BEGIN{srand();print rand()}'
      例:生成0-100之间的数awk 'BEGIN{srand();print int(rand()*100)}'

    • length([s])
      返回指定字符串的长度

    • sub(r,s,[t])
      对t字符串进行搜索r(支持正则表达式)表示的模式匹配的内容,并将第一个匹配的内容替换为s
      [root@hai7 /data]$echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
      2008-08:08 08:08:08
    • gsub(r,s,[t])
      对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容
      [root@hai7 /data]$echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
      2008-08-08 08-08-08
    • split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…
    用:做分隔符将2008:08:08 08:009:09分割为5段,分别存放在下标为1str[1]=2008str[2]=08...
    echo "2008:08:08 08:009:09"|awk '{split($0,str,":")}END{for(i in str)print i,str[i]}'
    4 009
    5 09
    1 2008
    2 08
    3 08 08
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-7f770a53f2.css" rel="stylesheet">
                </div>
    
    原文地址:https://www.cnblogs.com/duanxin1/p/9898153.html