Linux笔记 三剑客之awk

awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息
awk处理过程: 依次对每一行进行处理,然后输出
 
awk格式组成:
awk [options] '[pattern]{action}' [var1=value1 var2=value2 ...] file ...  或  awk [options] -f scripts  [var1=value1 var2=value2 ...] file ...
例:awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’ file
 
    options:[-F|-f|-v]   大参数,-F指定分隔符,-f调用脚本,-v定义变量 var=value
 
      pattern:  /RegExp/    正则表达式
 
                  条件表达式   关系运算符: > , < , >= , <= , == , != 
                                    字符串或数字比较
                                    ~ , !~ 模式匹配表达式
 
                  指定一个行的范围   /RegExp1/,/RegExp2/
                                            /^a/,/^b/
 
                   BEGIN    指定在第一条记录被处理前发生的动作action
                                用于初始化语句,变量定义,初始化格式
 
                   END    最后一条记录在被处理之后发生的动作
                            用于总结输出
 
                   空模式    对所有行应用指令
 
    action:由括在{}内的一或多条语句组成,语句之间用分号或者换行符分割,默认操作是打印所有表达式结果为真的文本行。                
                 变量或者数组赋值操作    var=value
                                                a["age"]="haha"
                                                a[1]="xxx"
 
                 格式化输出    print | printf
 
                 内置函数    sub | substr
 
                 控制流命令
                            if/else
                                if () {} else {}
                            for    用于遍历数组
                                    for (i in Array) {statements}
                            break    中断整个循环
                            continue    跳过循环后面的语句,直接开始下一次循环
                            while
                                    break
                                    continue
 
特殊要点:
$0           表示整个当前行
$1           每行第一个字段
NF          字段数量变量
NR          每行的记录号,多文件记录递增
FNR        与NR类似,不过多文件记录不递增,每个文件都从1开始
\t            制表符
\n           换行符
FS          BEGIN时定义分隔符
RS        输入的记录分隔符, 默认为换行符(即文本是按一行一行输入)
~            匹配,与==相比不是精确比较
!~           不匹配,不精确比较
==         等于,必须全部相等,精确比较
!=           不等于,精确比较
&&      逻辑与
||             逻辑或
+            匹配时表示1个或1个以上
/[0-9][0-9]+/   两个或两个以上数字
/[0-9][0-9]*/    一个或一个以上数字
FILENAME 文件名
OFS      输出字段分隔符, 默认也是空格,可以改为制表符等
ORS        输出的记录分隔符,默认为换行符,即处理结果也是一行一行输出到屏幕
-F'[:#/]'   定义三个分隔符
 
 
 
print & $0
print 是awk打印指定内容的主要命令
awk '{print}'  /etc/passwd   ==   awk '{print $0}'  /etc/passwd  
awk '{print " "}' /etc/passwd                                           //不输出passwd的内容,而是输出相同个数的空行,进一步解释了awk是一行一行处理文本
awk '{print "a"}'   /etc/passwd                                        //输出相同个数的a行,一行只有一个a字母
awk -F":" '{print $1}'  /etc/passwd 
awk -F: '{print $1; print $2}'   /etc/passwd                   //将每一行的前二个字段,分行输出,进一步理解一行一行处理文本
awk  -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd        //输出字段1,3,6,以制表符作为分隔符
 
 
-f指定脚本文件
awk -f script.awk  file
BEGIN{
FS=":"
}
{print $1}               //效果与awk -F":" '{print $1}'相同,只是分隔符使用FS在代码自身中指定
 
awk 'BEGIN{X=0} /^$/{ X+=1 } END{print "I find",X,"blank lines."}' test 
I find 4 blank lines.
 ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is",sum}'                    //计算文件大小
total size is 17487
 
 
-F指定分隔符
$1 指指定分隔符后,第一个字段,$3第三个字段, \t是制表符
一个或多个连续的空格或制表符看做一个定界符,即多个空格看做一个空格
awk -F":" '{print $1}'  /etc/passwd
awk -F":" '{print $1 $3}'  /etc/passwd                       //$1与$3相连输出,不分隔
awk -F":" '{print $1,$3}'  /etc/passwd                       //多了一个逗号,$1与$3使用空格分隔
awk -F":" '{print $1 " " $3}'  /etc/passwd                  //$1与$3之间手动添加空格分隔
awk -F":" '{print "Username:" $1 "\t\t Uid:" $3 }' /etc/passwd       //自定义输出  
awk -F: '{print NF}' /etc/passwd                                //显示每行有多少字段
awk -F: '{print $NF}' /etc/passwd                              //将每行第NF个字段的值打印出来
 awk -F: 'NF==4 {print }' /etc/passwd                       //显示只有4个字段的行
awk -F: 'NF>2{print $0}' /etc/passwd                       //显示每行字段数量大于2的行
awk '{print NR,$0}' /etc/passwd                                 //输出每行的行号
awk -F: '{print NR,NF,$NF,"\t",$0}' /etc/passwd      //依次打印行号,字段数,最后字段值,制表符,每行内容
awk -F: 'NR==5{print}'  /etc/passwd                         //显示第5行
awk -F: 'NR==5 || NR==6{print}'  /etc/passwd       //显示第5行和第6行
route -n|awk 'NR!=1{print}'                                       //不显示第一行
 
 
//匹配代码块
//纯字符匹配   !//纯字符不匹配    ~//字段值匹配    !~//字段值不匹配   ~/a1|a2/字段值匹配a1或a2   
awk '/mysql/' /etc/passwd
awk '/mysql/{print }' /etc/passwd
awk '/mysql/{print $0}' /etc/passwd                   //三条指令结果一样
awk '!/mysql/{print $0}' /etc/passwd                  //输出不匹配mysql的行
awk '/mysql|mail/{print}' /etc/passwd
awk '!/mysql|mail/{print}' /etc/passwd
awk -F: '/mail/,/mysql/{print}' /etc/passwd         //区间匹配
awk '/[2][7][7]*/{print $0}' /etc/passwd               //匹配包含27为数字开头的行,如27,277,2777...
awk -F: '$1~/mail/{print $1}' /etc/passwd           //$1匹配指定内容才显示
awk -F: '{if($1~/mail/) print $1}' /etc/passwd     //与上面相同
awk -F: '$1!~/mail/{print $1}' /etc/passwd          //不匹配
awk -F: '$1!~/mail|mysql/{print $1}' /etc/passwd        
 
 
条件表达式
==   !=   >   >=  
awk -F":" '$1=="mysql"{print $3}' /etc/passwd  
awk -F":" '{if($1=="mysql") print $3}' /etc/passwd          //与上面相同 
awk -F":" '$1!="mysql"{print $3}' /etc/passwd                 //不等于
awk -F":" '$3>1000{print $3}' /etc/passwd                      //大于
awk -F":" '$3>=100{print $3}' /etc/passwd                     //大于等于
awk -F":" '$3<1{print $3}' /etc/passwd                            //小于
awk -F":" '$3<=1{print $3}' /etc/passwd                         //小于等于
 
 
逻辑运算符
&& || 
awk -F: '$1~/mail/ && $3>8 {print }' /etc/passwd         //逻辑与,$1匹配mail,并且$3>8
awk -F: '{if($1~/mail/ && $3>8) print }' /etc/passwd
awk -F: '$1~/mail/ || $3>1000 {print }' /etc/passwd       //逻辑或
awk -F: '{if($1~/mail/ || $3>1000) print }' /etc/passwd 
 
 
数值运算
awk -F: '$3 > 100' /etc/passwd    
awk -F: '$3 > 100 || $3 < 5' /etc/passwd  
awk -F: '$3+$4 > 200' /etc/passwd
awk -F: '/mysql|mail/{print $3+10}' /etc/passwd                    //第三个字段加10打印 
awk -F: '/mysql/{print $3-$4}' /etc/passwd                             //减法
awk -F: '/mysql/{print $3*$4}' /etc/passwd                             //求乘积
awk '/MemFree/{print $2/1024}' /proc/meminfo                  //除法
awk '/MemFree/{print int($2/1024)}' /proc/meminfo           //取整
 
 
输出 分隔符OFS
awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt        
//输出字段6匹配WAIT的行,其中输出每行行号,字段4,5,6,并使用制表符分割字段
 
输出处理结果到文件
①在命令代码块中直接输出    route -n|awk 'NR!=1{print > "./fs"}'   
②使用重定向进行输出           route -n|awk 'NR!=1{print}'  > ./fs
 
 
格式化输出
netstat -anp|awk '{printf "%-8s %-8s %-10s\n",$1,$2,$3}' 
printf表示格式输出
%格式化输出分隔符
-8长度为8个字符
s表示字符串类型
打印每行前三个字段,指定第一个字段输出字符串类型(长度为8),第二个字段输出字符串类型(长度为8),
第三个字段输出字符串类型(长度为10)
netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-10s %-10s %-10s \n",$1,$2,$3}'
netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-3s %-10s %-10s %-10s \n",NR,$1,$2,$3}'
 
 
IF语句
必须用在{}中,且比较内容用()扩起来
awk -F: '{if($1~/mail/) print $1}' /etc/passwd                                      //简写
awk -F: '{if($1~/mail/) {print $1}}'  /etc/passwd                                  //全写
awk -F: '{if($1~/mail/) {print $1} else {print $2}}' /etc/passwd            //if...else...
 
awk -F: '{if($3>100) print "large"; else print "small"}' /etc/passwd
small
small
small
large
small
small
awk -F: 'BEGIN{A=0;B=0} {if($3>100) {A++; print "large"} else {B++; print "small"}} END{print A,"\t",B}' /etc/passwd 
                                                                                                                  //ID大于100,A加1,否则B加1
awk -F: '{if($3<100) next; else print}' /etc/passwd                         //小于100跳过,否则显示
awk -F: 'BEGIN{i=1} {if(i<NF) print NR,NF,i++ }' /etc/passwd   
awk -F: 'BEGIN{i=1} {if(i<NF) {print NR,NF} i++ }' /etc/passwd
另一种形式
awk -F: '{print ($3>100 ? "yes":"no")}'  /etc/passwd 
awk -F: '{print ($3>100 ? $3":\tyes":$3":\tno")}'  /etc/passwd
 
 
while语句
awk -F: 'BEGIN{i=1} {while(i<NF) print NF,$i,i++}' /etc/passwd 
7 root 1
7 x 2
7 0 3
7 0 4
7 root 5
7 /root 6
 
 
数组
netstat -anp|awk 'NR!=1{a[$6]++} END{for (i in a) print i,"\t",a[i]}'
netstat -anp|awk 'NR!=1{a[$6]++} END{for (i in a) printf "%-20s %-10s %-5s \n", i,"\t",a[i]}'
9523                               1     
9929                               1     
LISTEN                            6     
7903                               1     
3038/cupsd                   1     
7913                               1     
10837                             1     
9833                               1     
 
应用1
awk -F: '{print NF}' helloworld.sh                                                       //输出文件每行有多少字段
awk -F: '{print $1,$2,$3,$4,$5}' helloworld.sh                                 //输出前5个字段
awk -F: '{print $1,$2,$3,$4,$5}' OFS='\t' helloworld.sh                 //输出前5个字段并使用制表符分隔输出
awk -F: '{print NR,$1,$2,$3,$4,$5}' OFS='\t' helloworld.sh           //制表符分隔输出前5个字段,并打印行号
 
应用2
awk -F'[:#]' '{print NF}'  helloworld.sh                                                  //指定多个分隔符: #,输出每行多少字段
awk -F'[:#]' '{print $1,$2,$3,$4,$5,$6,$7}' OFS='\t' helloworld.sh   //制表符分隔输出多字段
 
应用3
awk -F'[:#/]' '{print NF}' helloworld.sh                                               //指定三个分隔符,并输出每行字段数
awk -F'[:#/]' '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12}' helloworld.sh     //制表符分隔输出多字段
 
应用4
计算/home目录下,普通文件的大小,使用KB作为单位
ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",sum/1024,"KB"}'
ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",int(sum/1024),"KB"}'         //int是取整的意思
 
应用5
统计netstat -anp 状态为LISTEN和CONNECT的连接数量分别是多少
netstat -anp|awk '$6~/LISTEN|CONNECTED/{sum[$6]++} END{for (i in sum) printf "%-10s %-6s %-3s \n", i," ",sum[i]}'
 
应用6
统计/home目录下不同用户的普通文件的总数是多少?
ls -l|awk 'NR!=1 && !/^d/{sum[$3]++} END{for (i in sum) printf "%-6s %-5s %-3s \n",i," ",sum[i]}'   
mysql        199 
root           374 
统计/home目录下不同用户的普通文件的大小总size是多少?
ls -l|awk 'NR!=1 && !/^d/{sum[$3]+=$5} END{for (i in sum) printf "%-6s %-5s %-3s %-2s \n",i," ",sum[i]/1024/1024,"MB"}'
 
应用7
输出成绩表
awk 'BEGIN{math=0;eng=0;com=0;printf "Lineno.   Name    No.    Math   English   Computer    Total\n";printf "------------------------------------------------------------\n"}{math+=$3; eng+=$4; com+=$5;printf "%-8s %-7s %-7s %-7s %-9s %-10s %-7s \n",NR,$1,$2,$3,$4,$5,$3+$4+$5} END{printf "------------------------------------------------------------\n";printf "%-24s %-7s %-9s %-20s \n","Total:",math,eng,com;printf "%-24s %-7s %-9s %-20s \n","Avg:",math/NR,eng/NR,com/NR}' test0

[root@localhost home]# cat test0 
Marry   2143 78 84 77
Jack    2321 66 78 45
Tom     2122 48 77 71
Mike    2537 87 97 95
Bob     2415 40 57 62

格式化输出详解
    print语句
        参数可以是变量,计算表达式,字符串常量
        字符串必须用双引号引起来
            echo 300 2 3 4 | awk '{print "hello" }'
        参数之间用逗号分隔
            echo 300 2 3 4 | awk '{print $1,$2 }'
        输出可以被重定向
            echo 300 2 3 4 | awk '$1 * $2 > 500{print $3+$4 >> "/tmp/test" }'
        输出和输入可以通过管道
            echo 300 2 3 4 | awk '$1 * $2 > 500{print $3+$4 | "grep 7" }'
        转义序列 用一个\与一个字母或数字来表示
            \n 换行
            \t 制表符
            \r 回车
            \047 八进制值47
 
  printf语句: 返回给标准输出一个带有格式的字符串;不会在行尾部自动换行;包含一个加""的控制字符串
        修饰符: -     左对齐,默认右对齐
                      +     数字带正负符号(+,-)
        格式说明转换符  %
                     c     字符character
                     s     字符串string
                     d     整数 digit
                     f     浮点数 float 
                    例:  echo alice 23 01 |awk ' {printf "The name is: %-15s ID is %8d\n",$1,$3}'
 
    练习:
 test文件内容:
 Mary  2143 78 84 77
Jack    2321 66 78 45
Tom    2122 48 77 71
Mike    2537 87 97 95
Bob    2415 40 57 62
输出成绩表:
 Lineno.  Name    No.    Math  English  Computer    Total
------------------------------------------------------------
1        Mary    2143    78      84        77        239
2        Jack    2321    66      78        45        189
3        Tom    2122    48      77        71        196
4        Mike    2537    87      97        95        279
5        Bob    2415    40      57        62        159
------------------------------------------------------------
Total:                  319    393      350
Avg:                    63.8    78.6      70
参考:
awk 'BEGIN{math=0;eng=0;com=0;printf "Lineno.  Name    No.    Math  English  Computer    Total\n";
printf "------------------------------------------------------------\n"}{math+=$3; eng+=$4;
com+=$5;printf "%-8s %-7s %-7s %-7s %-9s %-10s %-7s \n",NR,$1,$2,$3,$4,$5,$3+$4+$5}
END{printf "------------------------------------------------------------\n";
printf "%-24s %-7s %-9s %-20s \n","Total:",math,eng,com;printf "%-24s %-7s %-9s %-20s \n","Avg:",math/NR,eng/NR,com/NR}' test

  

 
 
 
awk编程
    1.函数
 
 -- 内置函数
 
  • 字符串函数
                sub
                    在记录中查找能够匹配正则表达式的最长最靠左子串,然后用替换串替换找到的子串
                    如果指定了目标串就对目标串查找替换,不指定则对当前记录处理
                    只对每行中的出现的第一个匹配进行替换
                    格式: sub(/RegExp/,"STR1"[,"STR2"]) FILENAME
                        搜索STR2中RegExp匹配的内容,替换成STR1
                        例
echo "abca" | awk '{sub(/a/,"b");print}'
echo "ab:cd" | awk -F: '{sub(/a/,"b",$1);print}'
 
                gsub
                    同sub,全局替换
 
                index
                    返回子串在字符串中第一次出现的位置,索引号从1开始计算,如无返回0
                    格式: index("字符串","查找子串")
                        例           
echo "ab:cd" | awk -F: '{print index($1,"a")}'
echo "ab:cd" | awk -F: '{print index($1,"b")}'  
 
                substr
                    返回从字符串指定位置开始的子串,
                    格式:
                        substr("字符串",开始位置,长度)
                        例
echo "abcdef" | awk '{print substr($0,1,3)}'
abc
 
                match
                    返回正则表达式在字符串中出现的位置,如果未出现则返回0
                    内置变量RSTART为匹配到子串在字符串中的起始位置
                    内置变量RLENGTH为子串的长度
                    可以通过substr提取出匹配到的子串
                    格式:match("字符串",/正则表达式/)
                    例
echo "abcdefABC" | awk '{match($0,/[[:upper:]]+/);print RSTART,RLENGTH;substr($0,RSTART,RLENGTH)}'
7 3
echo "abcdefABC" | awk '{match($0,/[[:upper:]]+/);print RSTART,RLENGTH;print substr($0,RSTART,RLENGTH)}' 7 3 ABC  
 
                split
                    使用第三个参数指定的字段分隔符将字符串拆分成一个数组
                    格式: split("字符串",数组名,"字段分隔符")
                    例子
echo "18/01/31" | awk '{split($0,date,"/");print date[1],date[2],date[3]}'
 
                length
                    返回指定字符串的长度
                    length("abcde")
 
  • 时间函数   
              mktime

  • 算术函数
                int() 取整
                rand() 随机数 0-1
                sqrt(x) 平方根
 
  • 其他函数
                system("CMD") 执行系统命令
 
-- 自定义函数
            格式
function_name(参数1,参数2,...){
    statements
    return expression
}

  

 
    2.变量
-- 用户自定义变量
            变量名由字母,数字,下划线组成,不能以数字开头
            awk可以从上下文推导出它的数据类型
            字符串变量要括在""中
            例
                echo | awk "BEGIN{a=4;b=2}{c=a*b; print c}"
 
-- 字段变量
            可以被赋值和修改
            引用的字段如果没有值,则被赋值为空串
            字段值发生新变化时,会以OFS的值作为字段分隔符重新计算$0变量
            $1
            $2
            ...
 
-- 内置变量
            ARGC
            ARGV
            ENVIRON
            FILENAME
            RLENGTH
            RSTART
            FNR
            NR
                awk -F: 'NR==FNR{student[$1]=$2}NR!=FNR{printf "%s:%s:%s\n",$1,$2,student[$1]}' test2 test1
            FS
            NF
            OFS
            ORS
            RS
 
 
 
    3.重定向
-- 输出重定向
            >
                例
                    echo | awk '{print "abc" > "/tmp/test"}'
            >>
 
-- 输入重定向
            函数: getline
            例
awk 'BEGIN{getline < "-";print}'
awk 'BEGIN{ getline < "test.txt"; print $0 }'
awk 'BEGIN{ while( getline line < "grep.test" ){ print line } }'
$ cat grep.test
hello
myboy
third
fourth

  

 
    4.管道
        可以在awk中打开一个管道,但同一时刻只能有一个管道存在
        用法
            awk {action | "CMD"}
        例
            echo -e "1\n3\n2" | awk '{print $0 | "sort"}'
 
 
 
    5.条件语句
-- if/else语句
if (expression) {
    statement;
    ...
}
else if (expression) {
    statement;
    ...
}
else {
    statement;
}
 
-- 循环
#while循环
while (expression) {
    statement;
    ...
}

#for循环 for (i=1;i<NF;i++){ statement; ... }
for (i in Array){ print Array[i] }

-- 循环控制:break | continue

 
    6.程序控制
        next
        exit N
 
 
    7.数组
        下标可以是数字或字符串
        数组元素不是顺序存储
        下标通常称为键(key)
        下标和关联数组
            变量可以作为数组下标
            字段值可以作为数组下标
            循环遍历数组中的元素
                for (i in Arrayname) {print Arrayname[i]}
       
练习
1.统计tcp的链接数
netstat -ant | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
netstat -ant

2.分别统计不同ip的tcp连接

netstat -ant | awk '/^tcp/ {n=split($(NF-1),array,":");
if(n<=2)++S[array[(1)]];else++S[array[(4)]];++s[$NF];++N}
END {for(a in S){printf("%-20s %s\n", a, S[a]);++I}printf("%-20s %s\n","TOTAL_IP",I);
for(a in s) printf("%-20s %s\n",a, s[a]);printf("%-20s %s\n","TOTAL_LINK",N);}'
 
 
 
 
 
 
 
 
 

猜你喜欢

转载自www.cnblogs.com/dumpling-z/p/11280994.html