zoukankan      html  css  js  c++  java
  • 三剑客 -- awk

    [root@hass-11 ~]# vmstat
    进程				内存				交换分区	磁盘		系统		CPU
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    																	
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     1  0      0 1140540   2076 625804    0    0     1     0   28   28  0  0 100  0  0
    
    

    小用法,使用awk来对文件随机抽取n行

    awk 'BEGIN{srand()} {print rand()"	"$0}' input_file | sort -nk 1 | head -n line_num | awk -F "	" '{print $2}' 
    

    简介

    awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

    awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。

    awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为样式扫描和处理语言。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。

    命令格式

    awk 选项 '操作' 文件
    或
    其他命令 | awk 选项 '操作'
    
    #花括号 {}不需要在程序中始终出现,但它们用于根据特定的模式'对一系列指令进行分组'。
    #要表示的正则表达式,用'斜杠'括起来
    #通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。
    #awk 相当于 grep + cut
    #awk 逐行读取,以指定分隔符分割字符串,赋值给变量
    

    选项

    -F 
    # 指定输入分隔符,awk会以该分隔符为准来切定位到的行,默认以空格或连续的空格为分隔符
    

    操作

    操作=定位+命令
    
    定位
    	行号
    	正则
        
    命令
    
    在读入所有行之前进行的操作        读一行执行一次的操作      所有行都读完后执行的操作
    BEGIN{}                         定位{}                     END{}
    
    条件类型 条 件 说 明
    awk保留字 BEGIN 在 awk 程序一开始,尚未读取任何数据之前执行。BEGIN 后的动作只在程序开始时执行一次
    awk保留字 END 在 awk 程序处理完所有数据,即将结束时执行?END 后的动作只在程序结束时执行一次
    关系运算符 > 大于
    < 小于
    >= 大于等于
    <= 小于等于
    == 等于。用于判断两个值是否相等。如果是给变童赋值,则使用"=”
    != 不等于
    A~B 判断字符串 A 中是否包含能匹配 B 表达式的子字符串
    A!~B 判断字符串 A 中是否不包含能匹配 B 表达式的子字符串
    正则表达式 /正则/ 如果在“//”中可以写入字符,则也可以支持正则表达式

    调用AWK的方式

    1.命令行方式
    awk [-F  field-separator]  'commands'  input-file(s)
    其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的文件。
    在awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是'空格'。
    
    2.shell脚本方式
    将所有的awk命令插入一个文件,并使awk程序可执行,然后'awk命令解释器'作为脚本的首行,一遍通过键入脚本名称来调用。
    相当于shell脚本首行的:#!/bin/sh
    可以换成:#!/bin/awk
    
    3.将所有的awk命令插入一个单独文件,然后调用:
    awk -f awk-script-file input-file(s)
    其中,-f选项加载awk-script-file中的awk脚本,input-file(s)跟上面的是一样的。
    

    awk中的正则

    <、<=、==、!=、>=、~匹配正则表达式(操作列)、!~不匹配正则表达式(取反)
    
    匹配:awk '{if ($4~/ASIMA/) print $0}' temp 表示如果第四个域包含 ASIMA,就打印整条
    
    精确匹配:awk '$3=="48" {print $0}' temp    只打印第 3 域等于"48"的记录
    
    不匹配:  awk '$0 !~ /ASIMA/' temp      打印整条不包含 ASIMA 的记录
    
    不等于:  awk '$1 != "asima"' temp
    
    小于:    awk '{if ($1<$2) print $1 "is smaller"}' temp
    
    设置大小写: awk '/[Gg]reen/' temp      打印整条包含 Green,或者 green 的记录
    
    任意字符: awk '$1 ~/^...a/' temp    打印第 1 域中第四个字符是 a 的记录,符号’^’代表行首,符合’.’代表任意字符
    
    或关系匹配: awk '$0~/(abc)|(efg)/' temp   使用| 时,语句尽量括起来
    并且关系匹配  awk -F: '$0~/(root)/ && $0~/(x)/'
    
    AND 与关系:  awk '{if ( $1=="a" && $2=="b" ) print $0}' temp
    		   awk -F: '$3 == $4 {print $0}'
    
    OR 或关系:   awk '{if ($1=="a" || $1=="b") print $0}' temp
    
    

    awk 的简单实用

    awk '{print $1}' /etc/passwd
    awk -F ':' '{print $1}' /etc/passwd				#-F指定域分隔符为':'
    awk -F ':' '{print $1"	"$7}' /etc/passwd		 #拼接域
    awk -F ':' '{$4=$3-$2; print $4}'				#拼接域
    awk -F: '/root/' /etc/passwd					#过滤
    awk -F: '/^root/' /etc/passwd
    awk -F: '/root/{print $7}' /etc/passwd 
    awk -F: 'NR==3{print $7}' /etc/passwd 
    awk -F: 'NR>=3 && NR<=5{print $7}' /etc/passwd 
    awk -F: 'NR<=3 || NR>=5{print $7}' /etc/passwd 
    awk 'BEGIN{print "hh"} {print $1} END{print "hh"}'
    
    # awk 中变量可以直接打印,不需要使用$
    # awk中字符串的拼接使用" ",可以在引号内添加空格,也可以使用逗号来加空格
    # awk中,正则要放到斜杠/ ,或者指定的边界内
    
    awk 'BEGIN{FS=":";}/root/{print $0} ' a.txt
    
    [root@egon day09]# awk 'BEGIN{FS=":";OFS="-"}{print NR,$0}' 6.txt 
    1-root:x:0:0:root:/root:/bin/bash
    2-bin:x:1:1:bin:/bin:/sbin/nologin
    3-daemon:x:2:2:root:/sbin:/sbin/nologin
    4-adm:x:3:4:adm:/var/adm:/sbin/nologin
    5-lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
                            
    [root@egon day09]# awk 'BEGIN{FS=":";OFS="-"}/root/{print NR,$1,$3,$5}' 6.txt 
    1-root-0-root
    3-daemon-2-root
    
    [root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $0} ' 6.txt
    root:x:0:0:root:/root:/bin/bash
    daemon:x:2:2:root:/sbin:/sbin/nologin
    [root@egon day09]# awk 'BEGIN{FS=":";}/root/{print NF} ' 6.txt
    7
    7
    [root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $NF} ' 6.txt
    /bin/bash
    /sbin/nologin
    [root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $(NF-1)} ' 6.txt
    /root
    /sbin
    
    #会显示每列的内容
    awk '(tot+=$3);END {print tot}' temp             
    awk '{(tot+=$3)};END {print tot}' temp 
    
    #文件长度相加
    ls -l|awk '/^[^d]/ {print $9"	"$5} {tot+=$5} END{print "totKB:" tot}'
    
    #awk内,支持变量直接运算,相当于expr
    

    如果只是显示/etc/passwd的账户和账户对应的shell,而账户与shell之间以逗号分割,而且在所有行添加列名name,shell,在最后一行添加"blue,/bin/nosh"。

    cat /etc/passwd |awk  -F ':'  'BEGIN {print "name,shell"}  {print $1","$7} END {print "blue,/bin/nosh"}'
    name,shell
    root,/bin/bash
    daemon,/bin/sh
    bin,/bin/sh
    sys,/bin/sh
    ....
    blue,/bin/nosh
    
    #awk工作流程是这样的:先执行BEGING,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。
    

    awk内置变量

    awk有许多内置变量用来设置环境信息,这些变量可以被改变,下面给出了最常用的一些变量。

    ARGC               命令行参数个数
    ARGV               命令行参数排列
    ENVIRON            支持队列中系统环境变量的使用
    FILENAME           awk浏览的文件名
    FNR                浏览文件的记录数
    FS                 设置输入域分隔符,等价于命令行 -F选项
    NF                 浏览记录的域的个数,列数,列
    NR                 已读行的记录数,行号
    OFS                输出域分隔符,默认是空格
    ORS                输出记录分隔符
    RS                 控制记录分隔符
    
    NR>=1 && NR<=3	   相当于sed中的 1,3中的行号定位
    ;				分割多条命令
    
    此外,$0变量是指整条记录。$1表示当前行的第一个域,$2表示当前行的第二个域,......以此类推。
    

    统计/etc/passwd:文件名,每行的行号,每行的列数,对应的完整行内容:

    #awk  -F ':'  '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd
    
    filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash
    
    [root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $1,$3} ' 6.txt
    root 0
    daemon 2
    [root@egon day09]# awk 'BEGIN{FS=":";}/root/{print "用户名:",$1,"用户id:",$3} ' 6.txt
    用户名: root 用户id: 0
    用户名: daemon 用户id: 2
    

    使用printf替代print,可以让代码更加简洁,易读

    awk  -F ':' '{printf("filename:%10s,linenumber:%s,columns:%s,linecontent:%s
    ",FILENAME,NR,NF,$0)}' /etc/passwd
     
    [root@egon day09]# date | awk '{printf "年:%s
    月:%s
    ",$NF,$2}'
    年:2020
    月:Sep
    [root@egon day09]# awk 'BEGIN{FS=":";}/root/{printf "用户名:%s 用户id:%s
    ",$1,$3} ' 6.txt
    用户名:root 用户id:0
    用户名:daemon 用户id:2
    
    # %-15s 表示占位符,左对齐,站位15个空格
    [root@egon day09]# awk -F: '{printf "用户名:%-15s|用户id:%-15s|用户组id:%-15s
    ",$1,$3,$4}' /etc/passwd
    
    [root@hass-11 ~]# df |awk 'NR>1{printf "对象:%s	已用百分比:%s
    ",$1,$5}'
    对象:devtmpfs	已用百分比:0%
    对象:tmpfs	已用百分比:0%
    

    print和printf

    awk中同时提供了print和printf两种打印输出的函数。
    
    其中print函数的参数可以是'变量、数值或者字符串'。字符串必须用'双引号'引用,参数用'逗号分隔'。如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与'输出文件的分隔符'的作用是一样的,只是后者是空格而已。
    
    printf函数,其用法和c语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。
    

    定位

    #正则表达式
    [root@hass-11 ~]# awk '/root/{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk '/^root/{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk '$2 ~ /root/{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk '$2 !~ /root/{print NR,$0}' /etc/passwd
    
    #比较表达式
    [root@hass-11 ~]# awk 'NR>=3 && NR<=5{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk 'NR>=3 || NR<=5{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk -F: '$3 > 500{print $1,$3}' /etc/passwd
    [root@hass-11 ~]# awk -F: '$1 !=root{print $1,$3}' /etc/passwd
    
    #行号
    [root@hass-11 ~]# awk 'NR=5{print NR,$0}' /etc/passwd
    
    #幂数运算,+ - * / %(模) ^(幂2^3)
    [root@hass-11 ~]# awk -F: '$4*12 >40{print $0}' /etc/passwd
    [root@hass-11 ~]# awk -F: 'NR%2 == 1{print $0}' /etc/passwd	#取奇数行
    
    #且 或 非
    [root@hass-11 ~]# awk 'NR>=3 && NR<=5{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk 'NR>=5 || NR<=3{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk '!(NR>=3 && NR<=4){print NR,$0}' a.txt 
    [root@hass-11 ~]# awk 'NR>=3,NR<=5{print NR,$0}' /etc/passwd	#
    [root@hass-11 ~]# awk 'NR>=3,/yy/{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk '/root/,NR<=5{print NR,$0}' /etc/passwd
    [root@hass-11 ~]# awk '/root/,/yy/{print NR,$0}' /etc/passwd #可以多次匹配
    
    #awk中,定位既可以在{}外,也可以在{}内,只不过在{}内要加if判断
    

    awk 内置字符串函数

    gsub(r,s)              在整个$0中用s替代r  
      
    awk 'gsub(/name/,"xingming") {print $0}' temp  
      
    gsub(r,s,t)           在整个t中用s替代r  
      
    index(s,t)             返回s中字符串t的第一位置  
      
    awk 'BEGIN {print index("Sunny","ny")}' temp     返回4  
      
    length(s)               返回s的长度  
      
    match(s,r)             测试s是否包含匹配r的字符串  
      
    awk '$1=="J.Lulu" {print match($1,"u")}' temp    返回4  
      
    split(s,a,fs)         在fs上将s分成序列a   
      
    awk 'BEGIN {print split("12#345#6789",myarray,"#")"' 
     
    返回3,同时myarray[1]="12", myarray[2]="345", myarray[3]="6789"  
      
    sprint(fmt,exp)        返回经fmt格式化后的exp  
      
    sub(r,s)   从$0中最左边最长的子串中用s代替r(只更换第一遇到的匹配字符串)  
      
    substr(s,p)         返回字符串s中从p开始的后缀部分  
      
    substr(s,p,n)       返回字符串s中从p开始长度为n的后缀部分
    

    awk编程

    变量和赋值

    除了awk的内置变量,awk还可以自定义变量。

    下面统计/etc/passwd的账户人数

    awk '{count++;print $0;} END{print "user count is ", count}' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    ......
    user count is  40
    
    #count是自定义变量。其实print只是一个语句,多个语句,以;号隔开。
    

    这里没有初始化count,虽然默认是0,但是妥当的做法还是初始化为0:

    awk 'BEGIN {count=0;print "[start]user count is ", count} {count=count+1;print $0;} END{print "[end]user count is ", count}' /etc/passwd
    [start]user count is  0
    root:x:0:0:root:/root:/bin/bash
    ...
    [end]user count is  40
    

    统计某个文件夹下的文件占用的字节数

    ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}'
    [end]size is  8657198
    

    如果以M为单位显示:

    ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}' 
    [end]size is  8.25889 M
    
    #注意,统计不包括文件夹的子目录。
    

    条件语句

    awk中的条件语句是从C语言中借鉴来的,见如下声明方式:

    if (expression) {
        statement;
        statement;
        ... ...
    }
    
    if (expression) {
        statement;
    } else {
        statement2;
    }
    
    if (expression) {
        statement1;
    } else if (expression1) {
        statement2;
    } else {
        statement3;
    }
    
    # 1、单分支
    [root@egon day10]# awk -F: '$3 > 1000{print $0}' /etc/passwd
    awk -F: '{if ($3>1000) {count++;}}END{print count}' /etc/passwd
    
    # 2、双分支
    awk -F: '{if ($3==0) {x++} else {y++}}END{printf "管理用户个数:%s 其他用户个数:%s
    ",x,y}' /etc/passwd
    
    # 3、多分支
    awk -F: '{if ($3==0) {x++} else if($3>1000) {y++} else {z++}}END{printf "管理用户个数:%s 普通用户个数:%s 系统用户:%s
    ",x,y,z}' /etc/passwd
    
    #awk中判断结束不需要使用fi 结尾
    #awk中,{}可以嵌套
    #分号表示不同命令的分割
    

    统计某个文件夹下的文件占用的字节数,过滤不是4096大小的文件(一般都是文件夹):

    ls -l |awk 'BEGIN {size=0;print "[start]size is ", size} {if($5!=4096){size=size+$5;}} END{print "[end]size is ", size/1024/1024,"M"}' 
    [end]size is  8.22339 M
    

    循环语句

    awk中的循环语句同样借鉴于C语言,支持while、do/while、for、break、continue,这些关键字的语义和C语言中的语义完全相同。

    #循环
    [root@hass-11 ~]# awk -F: '/root/{count=0;while(count < 3) {count++;print $0}}' /etc/passwd
    [root@hass-11 ~]# awk -F: '/root/{for(i=0;i<3;i++) {print $0}}' /etc/passwd
    
    
    #循环+判断+continue
    [root@hass-11 ~]# awk -F: '/root/{count=0;while(count < 3) {count++;if (count==1) {continue};print $0}}' /etc/passwd
    [root@hass-11 ~]# awk -F: '/root/{for(i=0;i<3;i++) {if(i==1) {continue};print $0}}' /etc/passwd
    
    #循环+判断+break
    [root@hass-11 ~]# awk -F: '{count=0;while(count < 3) {count++;if (count==2) {break};print $0}}' /etc/passwd
    [root@hass-11 ~]# awk -F: '/root/{for(i=0;i<3;i++) {if(i==1) {break};print $0}}' /etc/passwd
    
    #死循环
    [root@hass-11 ~]# awk -F: '{while(1 !=2) {print $0}}' /etc/passwd
    
    
    #计算的话,count一定要定义在 BEGIN
    #循环的话,count一定要定义在 第二段
    # xxx=0,在awk中可以省略,直接使用
    

    数组

    因为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格里。由于hash不是顺序存储,因此在显示数组内容时会发现,它们并不是按照你预料的顺序显示出来的。数组和变量一样,都是在使用时自动创建的,awk也同样会自动判断其存储的是数字还是字符串。一般而言,awk中的数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。

    显示/etc/passwd的账户

    #普通数组的定义和遍历
    awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd
    0 root
    1 daemon
    2 bin
    3 sys
    4 sync
    5 games
    ......
    
    
    #name[0] 表示数组中的第一个元素
    #awk定义的数组,在命令行中看不到
    

    数组+for循环的另外一种格式

    awk -F: 'BEGIN{i=0} {user[i++]=$1}END{print user[0]}' /etc/passwd
        
    awk -F: '{user[i++]=$1}END{print user[0]}' /etc/passwd
    

    遍历数组

    # 有序遍历
    awk -F: 'BEGIN{i=0} {user[i++]=$1} END{for(j=0;j<NR;j++) {print j,user[j]}}' /etc/passwd
    awk -F: 'BEGIN{i=1} {user[i++]=$1} END{for(j=1;j<=NR;j++) {print j,user[j]}}' /etc/passwd
    
    for ((i=0;i<28;i++))	#固定格式,对应user0 user1 user2
    do
       echo ${user[$i]}
    done
    
    # 无序遍历
    awk -F: 'BEGIN{i=1}{user[i++]=$1} END{for (x in user) {print user[x]}}' /etc/passwd
    awk -F: 'BEGIN{i=0}{user[$1]=$3} END{for (k in user) {print k,user[k]}}' /etc/passwd
        
    #普通数组既可以有序遍历,又可以无序遍历
    #关联数组只能无序遍历
    

    脚本中的awk

    !/bin/awk -f   
    # all comment lines must start with a hash '#'  
    # name: student_tot.awk  
    # to call: student_tot.awk grade.txt  
    # prints total and average of club student points  
    # print a header first  
    BEGIN  
     {  
       print "Student    Date   Member No.  Grade  Age  Points  Max"  
       print "Name  Joined Gained  Point Available"  
       print"========================================================="  
     }  
      
    # let's add the scores of points gained  
    (tot+=$6);  
      
    # finished processing now let's print the total and average point  
    END  
    {  
        print "Club student total points :" tot  
        print "Average Club Student points :" tot/N  
    }
    

    使用pid文件限制重复启动进程

    [root@egon day10]# cat proc.sh 
    #!/bin/bash
    pidfile='/tmp/proc.pid'
    
    if [ -f $pidfile ];then
        pid=`cat $pidfile`
        ps $pid &>/dev/null
        [ $? -eq 0 ] && echo "脚本正在运行..." && exit 1
    fi
    
    echo "$$" > $pidfile
    
    echo "脚本正在运行。。。"
    sleep 500
    echo "脚本运行完毕。。。"
    
    rm -rf $pidfile
    
    

    shell队列实现并发控制

    https://blog.51cto.com/egon09/1754317

    awk编程的内容极多,这里只罗列简单常用的用法,更多请参考 http://www.gnu.org/software/gawk/manual/gawk.html

    https://blog.51cto.com/egon09

  • 相关阅读:
    桥接模式
    单例模式
    迭代器模式
    组合模式
    备忘录模式
    适配器模式
    状态模式
    观察者模式
    golang 字符串统计
    go bytes缓冲区使用介绍 -转自https://www.cnblogs.com/--xiaoyao--/p/5122138.html
  • 原文地址:https://www.cnblogs.com/syy1757528181/p/13619145.html
Copyright © 2011-2022 走看看