• Linux之强大的gawk


    awk介绍

    awk是Linux文本处理工具三剑客之一,它是一种报表生成器,用于对文件内容进行各种"排版",然后进行格式化显示。

    在Linux上,我们使用的是GNU awk 简称gawk,其实,gawk就是awk的链接文件,因此,在系统上使用awk和gawk是一样的

    gawk是一种过程式编程语言,它支持条件判断,数组,循环等各种编程语言中所有可以使用的功能,因此,我们还可以把gawk称为一种脚本语言解释器

    awk是模式扫描和处理语言,该程序通常由: BEGIN语句块、能够使用模式匹配的通用语句块 、END语句块,共3部分组成 

    awk的三种运行方式:

    (1) awk 命令行

    awk

    (2) awk 程序文件

    awk -f /path/from/awk_script

    (3) awk 脚本

    #!/bin/awk -f

    awk 的基本用法

    1. awk [options] 'program' var=value file ...
    2. awk [options] -f programfile var=value file ...
    3. awk [options] 'BEGIN{ action;...} pattern{ action;...} END{ action;... }' file ... 

    awk [options] 'program' FILE1 FILE2 ...

        program : PATTERN {ACTION STATEMENT}

            program : 编程语言,通常是被单引号或双引号引中

            PATTERN : 模式,决定动作语句何时触发及触发事件 (BEGIN,END)

            ACTION STATEMENT :动作语句,可以是由多个语句组成,各语句之间使用分号分割,,放在{}内指明 (print, printf)

        options : 

            -F :指明输入字段的分隔符,默认为空白字符

            -v var=value : 自定义变量

    分隔符,域和记录:

        awk执行时,由分隔符分割的字段(域)标记 $1,$2,...$n 称为域标识。$0表示所有域,注意,和shell变量中的$符号含义不同

        文件的每一行称为记录

        省略action,则默认执行 print $0 的操作

    awk工作原理

    第一步 : 执行BEGIN{ACTION;...}语句块中的语句

    第二步 : 从文件或标准输入(stdin)读取一行,然后执行 pattern{action;...}语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕

    第三步 : 当读取至输入流末尾时,执行END{ACTION;...}语句块

    BEGIN 语句块在awk开始从输入流中读取之前被执行,这是一个可选的语句块,比如变量初始化,打印输出表格的表头等语句通常可以写在BEGIN语句块中

    END 语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块

    pattren 语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{print},既打印每一个读取到的行,awk读取的每一行都会执行该语句块

    awk命令

    1, print

    用法: print item1,item2,...

        item : 字符串,用引号引用

        变量 : 显示变量的值,可以直接使用变量的名进行引用

        数值 : 无须加引号

    要点: 

        (1) : 逗号分隔符

        (2) : 输出的各item可以是字符串,也可以是数值,当前记录的字段,变量或awk的表达式

        (3) : print后面的item省略时,相当于运行 print $0 ,用于输出整行

    示例:

    1. awk '{print "hello awk"}' /etc/fstab
    2. awk -F: '{print $1,$3}' /etc/passwd
    3. awk -F: '{print $1"\t"$3}' /etc/passwd

        

    2, 变量 

        变量分为内建变量和自定义变量

    2.1 内建变量

        FS : 输入字段分隔符,默认为空白字符

    1. awk -v FS=":" '{print $1,$3}' /etc/passwd
    2. awk -F: '{print $1,$3}' /etc/passwd

        OFS : 输出字段分隔符,默认为空白字符

    awk -v FS=":" -v OFS="---" '{print $1,$3}' /etc/passwd

        RS : 输入记录分隔符,指定输入时的换行符,原换行符仍有效

    awk -v RS=':' '{print}' /etc/passwd

        ORS : 输出记录分隔符,输出时用指定符号代替换行符

    awk -v ORS='####' '{print}' /etc/passwd

        NF : 字段的数量

    1. awk -F: '{print NF}' /etc/passwd
    2. awk -F: '{print $(NF-1)}' /etc/passwd

        NR : 行号

    1. awk '{print NR}' /etc/fstab
    2. awk 'END{print NR}' /etc/fstab

        FNR : 分别统计各文件的行号

    awk '{print FNR}' /etc/fstab /etc/passwd

        FILENAME : 当前文件名

    awk 'END{print FILENAME}' /etc/fstab

        ARGC : 命令行参数的个数,awk也算一个参数

    awk '{print ARGC}' /etc/fstab /etc/passwd 

        ARGV : 数组,保存的是命令行所给定的各参数

    awk 'BEGIN{print ARGV[0]}' /etc/fstab 

     

    2.2 自定义变量

        (1) -v var=vaule 变量名区分字符大小写

        (2) 在program中直接定义

    示例:

    1. awk -v test='hello awk' 'BEGIN{print test}'
    2. awk 'BEGIN{test="hellp";print test}'

    3, printf 命令

    格式化输出 : printf "FORMAT", item1.item2,...

        (1) 必须指定FORMAT

        (2) 不会自动换行,需要显示给出换行控制符,\n

        (3) FORMAT 中需要分别为后面每个item指定格式符

    格式符: 与 item 一一对应 

    1. %c : 显示字符的ascii码
    2. %d,%i : 显示十进制整数
    3. %e,%E : 科学计数法数值显示
    4. %f : 显示为浮点数
    5. %g,%G : 以科学计数法或浮点形式显示数值
    6. %s : 显示字符串
    7. %u : 无符号整形
    8. %% : 显示%自身

    修饰符:

    #[.#] : 第一个数字控制显示的宽度,第二个#表示小数点后的精度  %3.1f  

    - : 左对齐 (默认右对齐)  %-15s

    + : 显示数值的符号  %+d

    示例:

    awk -F: '{printf "%-15s%d\n",$1,$3}' /etc/passwd

    awk -F: '{printf "ername: %s,UID:%d\n",$1,$3}' /etc/passwd

    4,awk的操作符

    操作符:

    算术操作符:

    1. +;-;*;/;^;%
    2. -x :转换正负
    3. +x :把字符串转换为数值

    字符串操作符:没有符号的操作符表示字符串连接

    赋值操作符:

    =,+=,-=,*=,/=,%=,^=,--,++

    比较操作符:

    >,>=,<=,!=,==

    模式匹配符:

    1. ~ : 是否匹配
    2. !~ : 不能够匹配
    3.         # awk –F: '$0 ~ /root/{print $1}'  /etc/passwd 
    4.         # awk  '$0  !~ /root/'   /etc/passwd

    逻辑操作符:

    &&;||;!

    示例:

    awk -F: '$3>=500{print $1,$3}' /etc/passwd

        

    1. awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd 
    2. awk -F: '!($3==0) {print $1}' /etc/passwd 
    3. awk -F: '!($3>=500) {print $3}}' /etc/passwd 

    函数调用: function_name(arg1,arg2,...)

    条件表达式(三目表达式)

    selector?if-true-expression:if-false-expression

    示例:

    tail -4 /etc/passwd|awk -F: '{$3>=500?usertype="common users":usertype="system users";printf "%-15s%s\n",$1,usertype}'

        

    5,PATTERN 模式

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

    (1) 如果未指定,空模式,匹配文本的每一行

    (2) [!]/regular expression/ : 仅处理能够模式匹配到的行,需要用 / /括起来

    1. awk '/^UUID/{print $1}' /etc/fstab
    2. awk '!/^UUID/{print $1}' /etc/fstab

    (3) relational expression : 关系表达式,结果有'真'有'假',结果为真时才会被处理

        真 : 结果为非0值,非空字符串

        假 : 结果为空字符串或0值

    示例:

    1.  awk  '!0'  /etc/passwd ; awk  '!1'   /etc/passwd 
    2.  awk –F: '$3>=1000{print $1,$3}'  /etc/passwd 
    3.  awk -F: '$3<1000{print $1,$3}'  /etc/passwd 
    4.  awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd 
    5.  awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd

    (4) line ranges : 行范围

        

    startline,endline : /part1/,/part2/ 不支持直接给出数字

    格式:

    awk -F: '/^root/,/^nobody/{print}' /etc/passwd

    awk -F: 'NR>10&&NR<=15{print}' /etc/passwd

    (5) BEGIN/END 模式

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

    示例:

    1. awk -F: 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "end file"}' /etc/passwd 
    2.  awk -F: '{print "USER USERID“;print $1":"$3} END{print "end file"}' /etc/passwd 
    3.  awk -F: 'BEGIN{print "  USER  UID  \n --------------"}{print $1,$3}' /etc/passwd 
    4.  seq 10 |awk 'i=0' 
    5.  seq 10 |awk 'i=1'
    6.  seq 10 | awk 'i=!i'
    7.  seq 10 | awk '{i=!i;print i}'  
    8.  seq 10 | awk '!(i=!i)' 
    9.  seq 10 |awk -v i=1 'i=!i'

    6,常用action

    (1) Expression :算术,比较表达式等

    (2) Control statements : if,while等

    (3) Compound statements : 组合语句

    (4) input statements

    (5) output statements : print等

    7,控制语句

    1. if(condition){statements;...}
    2. if(condition){statements;...}else{statements;...}
    3. while(condition) {statements;...}
    4. do{statements;...} while(condition)
    5. for(expr1;expr2;expr3){statements;...}
    6. break
    7. continue
    8. delete array[index]
    9. delete array
    10. exit
    11. {statements;...} 组合语句

    7.1 if-else:

    语法: 

    1. if(condition)statement [else statement]
    2. if(condition1){statement1}else if(condition2){statement2} else{statement3} 
    1. awk -F: '{if($3>=500)print $1,$3}' /etc/passwd
    2. awk -F: '{if($3>=300) {printf "ok:%s\n",$1} else printf "no:%s\n",$1}' /etc/passwd
    3. df -h | awk -F'%' '/^\/dev/{print $1}'|awk '{if($NF>4)print $1}'

    使用场景:对awk取得的整行或某个字段做条件判断

    1. awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd 
    2. awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd 
    3. awk '{if(NF>5) print $0}' /etc/fstab 
    4. df -h|awk -F% '/^\/dev/{print $1}'|awk '$NF>=80{print $1,$5}'

    7.2 while 循环:

    语法: while(condition)statement

        条件为真,进入循环,条件为假,退出循环

    使用场景:对一行内多个字段逐一类似处理时使用:对数组中的各元素逐一处理时使用

        length() 字段长度

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

    7.3 do-while 循环:

    语法: do {statement;...} while(condition)

        意义:无论真假,至少执行一次循环体

    示例:

    1. [root@centos6 ~]# awk 'BEGIN{sum=0;i=0;do{sum+=i;i++}while(i<=100)print sum}'
    2. 5050

    7.4 for 循环:

    语法: for(expr1;expr2;expr3) {statement;...}

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

    特殊用法:

        可以遍历数组中的每一个元素

        语法: for(var in array){condition}

    性能比较(awk 和 shell 中for循环所需的时间)

    1. time (sum=0;for i in {1..10000};do let sum+=i;done;echo $sum)
    2. time (awk 'BEGIN{sum=0;i=0;do{sum+=i;i++}while(i<=10000)print sum}')
    3. time (sum=0;for((i=0;i<=10000;i++));do let sum+=i;done;echo $sum)

    7.5 switch语句

    语法: switch(expression){case VALUE or /REGEXP/:statement;...;default:statement}

    7.6 break和continue

    7.7 next

    提前结束对本行的处理而直接进入下一行

    awk -F: '{if($3%2!=0) next;print $1,$3}' /etc/passwd

    8,数组

    关联数组: array[index-expression]

    index-expression :

    (1) : 可使用任意字符串,字符串要使用双引号括起来

    (2) : 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为"空串"

    若要判断数组中是否存在某元素,要使用"index in array"格式进行遍历

    1. awk 'BEGIN{weekdays["mon"]="Monday"; weekdays["tue"]="Tuesday";print weekdays["mon"]}' 
    2. awk '!a[$0]++' dupfile

    若要遍历数组中的每个元素,要使用for循环

    for(var in array){for-body}

    注意: var 会遍历array的每个索引

    1. awk 'BEGIN{k["a"]="aaa";k["b"]="bbb";for(i in k)print k[i]}'
    2. netstat -tan | awk 'NR>2{k[$NF]++;}END{for(i in k)print k[i],i}'

    9,函数

    数值处理:

        rand();返回0和1之间一个随机数,这个产生的随机数是不变的

        

        如果要产生随机数,需要srand() 函数

    awk 'BEGIN{srand() ;print int(rand()*1000000)}' #有点问题

        

    字符串处理:

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

        sub(r,s,[t]) : 对t字符串进行搜索r表示的模式匹配的内容,并将第一次匹配的内容替换为s

        

         gsub(r,s,[t]) : 对t字符串进行搜索r表示的模式匹配的内容,并将匹配的所有内容都替换为s

        

        split(s,array,[r]) : 以r为分隔符,切割字符s,并将切割后的结果保存到array所表示的数组中,第一个索引值为1,不是0;第二个索引值为2;...

    netstat -ant | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++ }END{for(i in count){print i,count[i]}}'

    自定义函数:

    函数:

    1. function name (parameter,parameter,...){
    2.         statements
    3.         return expression
    4. }

    示例:

        #cat fun.awk

    1. function max(a,b){
    2.         a>b?var=a:var=b
    3.         return var
    4. }
    5. BEGIN{a=3;b=2;print max(a,b)}
    awk -f fun.awk  

        

    awk中调用shell命令

    system 命令

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

    awk脚本

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

    比如:

      

    向awk脚本参数

    格式: awkfile var=value var2=value2 ... InputFile

    示例:

       

    练习

    1、统计/etc/fstab文件中每个文件系统类型出现的次数 

    2、统计/etc/fstab文件中每个单词出现的次数

    3,求每班总成绩和平均成绩,成绩表格式如下

    name class score

    wang   1    100

    zhang  2    90

    li     1    80

    awk 'BEGIN{print "班级 总成绩 平均成绩"}{if($2==1){sum1+=$3;count1++}else if($2==2) {sum2+=$3;count2++}}END{printf " 1 %7d   %3.2f\n",sum1 ,sum1/count1;printf " 2 %7d   %3.2f\n",sum2 ,sum2/count2}' test

  • 相关阅读:
    卡尔曼家族从零解剖-(05)卡尔曼滤波→公式推导,应用通俗讲解,c++代码示例
    [ 渗透测试面试篇 ] 渗透测试面试题大集合(详解)(二)XSS注入相关面试题
    流水线上的农民:我在工厂种蔬菜
    模板学堂丨Zabbix监控告警大屏
    gs_moment
    【Linux编程Shell自动化脚本】03 shell四剑客(find、sed、grep、awk)
    100个Java工具类之61:队列类Queue
    vmware17.0|ubuntu22.04.0 解决灰色Vmware Tool 无法重新安装和 无法和win11相互拖拽文件问题
    《使用AADL的模型基工程》读书笔记(一)
    读《沟通的方法》推荐序有感
  • 原文地址:https://blog.csdn.net/ly1358152944/article/details/126342314