Linux学习26-文本处理工具AWK

文本处理工具awk

1. 简介

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

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

    1. 分割符、域和记录

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

      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}'
      
  • 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

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\naa,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\naa为第三个字段
      aa==bb
      
  • ORS:输出记录分隔符,输出时用指定符号代替换行符

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

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

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

    $awk -F: '{print NR,$1}' /etc/passwd
    45 named     <==相当于多了个行号
    
  • 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文件最后一个记录
    
  • FILENAME:当前文件名
    承上例,如果觉得显示不清楚,可以加上文件名

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

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

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

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="\n------\n" '{print USER":"$1"\n"UID":"$3}' /etc/passwd  /etc/group 
      ------
      username:screen
      userid:84
      ------
      username:named
      userid:25
      ------
      
    2. 示例:使用格式2自定义

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

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

4. action中的printf命令

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

    • 必须指定FORMAT
    • 不会自动换行,需要显式给出换行控制符,\n
    • 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\n",$1,$3}’ /etc/passwd

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

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

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

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

示例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宽度起作用了,说明宽度如果小于实际占用值,会以实际宽度为准

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值,再递增

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

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
  • 条件表达式(三目表达式):
    (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\n" ,$1,type}' /etc/passwd
root sys
nfsnobody user

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
      
    3. 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’
          
        4. 取偶数行或奇数行
          打印奇数行,因为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'打印偶数行
    4. 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

    5. 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
        

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的要写在花括号外面

示例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

示例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  清空防火墙

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

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

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

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

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

示例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

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

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

示例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

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}

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

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

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

示例2:从1+到100相加

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

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,就不往后加了
    
  • 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
  • next:
    提前结束对本行处理而直接进入下一行处理(awk自身循环)
打印  奇数行
awk -F: '{if($3%2!=0)next;print NR,$1,$3}' /etc/passwd

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

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

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

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

5、bc计算

$time seq -s + 100000|bc
5000050000

real    0m0.041s
user    0m0.036s
sys 0m0.007s

9. awk数组

9.1 awk支持关联数组

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

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

•awk ‘!arr[ 0 ] + + d u p f i l e 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 ] + + ; p r i n t 0, arr[$0]}’ dupfile

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

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

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

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

统计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

分别统计男生女生的平

[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

方法二

[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

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

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

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   

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

猜你喜欢

转载自blog.csdn.net/free050463/article/details/82630763