描述awk命令和函数的用法及示例

一、简介

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

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

二、命令的用法

命令格式

gawk [options] 'program...' FILE ...

常见选项

-F fs:指明字段分隔符;
-v var=value:自定义变量;

program语句

常见格式:PATTERN{ACTION STATEMENTS} #语句之间用分号分隔

  1. print
  • 逗号分隔符
  • 输出的各item可以是字符串、数值、记录的字段、变量或awk表达式
  • 省略item,打印全部字段,相当于print $0。
  1. 变量
  • 内建变量
    FS:输入字段分隔符,默认为空白字符;
    OFS:输出字段分隔符,默认为空白字符;
    RS:输入时的换行符;
    ORS:输出时的换行符;
    NF:每行的字段数,$NF表示最后一个字段;
    NR:表示行数;
    FNR:若提供多个文件,则分别计算各文件的行数;
    FILENAME:当前文件名;
    ARGC:命令行参数的个数;
    ARGV:参数数组,保存的是命令行给定的各参数;
  • 自定义变量
    (1) -v var=value #变量名区分字符大小写;
    (2) 在program中直接定义
  1. printf命令
  • 格式化输出:printf FORMAT, item1, item2, ...
    (1) FORMAT必须给出;
    (2) 不会自动换行,需要显式给出换行控制符,\n
    (3) FORMAT中需要分别为后面的每个item指定一个格式化符号;
  • 格式符:
    %c: 显示字符的ASCII码;
    %d, %i: 显示十进制整数;
    %e, %E: 科学计数法数值显示;
    %f:显示为浮点数;
    %g, %G:以科学计数法或浮点形式显示数值;
    %s:显示字符串;
    %u:无符号整数;
    %%: 显示%自身;
  • 修饰符:
    #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度。 %3.1f
    -: 左对齐
    +:显示数值的符号
  1. 操作符
    算术操作符: x+y, x-y, x*y, x/y, x^y, x%y,-x,+x
    字符串操作符:没有符号的操作符,字符串连接
    赋值操作符:=, +=, -=, *=, /=, %=, ^=, ++, --
    比较操作符:>, >=, <, <=, !=, ==
    模式匹配符::是否匹配,!:是否不匹配
    逻辑操作符:&&,||,!
    函数调用:function_name(argu1, argu2, ...)
    条件表达式:selector?if-true-expression:if-false-expression

  2. PATTERN
    (1) empty:空模式,匹配每一行;
    (2) /regular expression/:仅处理能够被此处的模式匹配到的行;
    (3) relational expression: 结果为真才会被处理;真为非0值,非空字符串;
    (4) line ranges:行范围,startline,endline:/pat1/,/pat2/
    注意: 不支持直接给出数字的格式
    ~]# awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd
    (5) BEGIN/END模式
    BEGIN{}: 仅在开始处理文件中的文本之前执行一次;
    END{}:仅在文本处理完成之后执行一次;

  3. 常用的action
    (1) Expressions
    (2) Control statements:if, while等;
    (3) Compound statements:组合语句;
    (4) input statements
    (5) output statements

  4. 控制语句

  • if(condition) {statments}
    if(condition) {statments} else {statements}
    while(conditon) {statments}
    do {statements} while(condition)
    for(expr1;expr2;expr3) {statements}
    break
    continue
    delete array[index]
    delete array
    exit
    { statements }

  • if-else:if(condition) statement [else statement]
    ~]# awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
    ~]# awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
    ~]# awk '{if(NF>5) print $0}' /etc/fstab
    ~]# df -h | awk -F[%] '/^/dev/{print $1}' | awk '{if($NF>=20) print $1}'
    使用场景:对awk取得的整行或某个字段做条件判断;

  • while:while(condition) statement
    条件“真”,进入循环;条件“假”,退出循环;
    ~]# awk '/^[[:space:]]linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
    ~]# awk '/^[[:space:]]
    linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg
    使用场景:对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用;

  • do-while:do statement while(condition)
    意义:至少执行一次循环体

  • for:for(expr1;expr2;expr3) statement
    for(variable assignment;condition;iteration process) {for-body}
    ~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg

    特殊用法:
    能够遍历数组中的元素;
    语法:for(var in array) {for-body}

  • switch
    switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}

  • break和continue
    break [n]
    continue

  • next
    提前结束对本行的处理而直接进入下一行;
    ~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd

  • array

关联数组:array[index-expression]
index-expression:
(1) 可使用任意字符串;字符串要使用双引号;
(2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”;
若要判断数组中是否存在某元素,要使用"index in array"格式进行;
weekdays[mon]="Monday"
若要遍历数组中的每个元素,要使用for循环;
for(var in array) {for-body}
~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
注意:var会遍历array的每个索引;
如果数组不存在,我们引用数组,会创建,值为空,引用数组的值为0。

  • 函数

1.内置函数
数值处理:
rand():返回0和1之间一个随机数
字符串处理:
length([s]):返回指定字符串的长度;
sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容;t中有没有r,有替换成s
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容;
split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中;
~]# netstat -tan | awk '/^tcp>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'

2.自定义函数

三、入门实例

1.把文本分片后排列整齐,并加上标签
[root@localhost sctript]# cat awk01 
 Mike Harrington:(510) 548-1278:250:100:175
 Christian Dobbins:(408) 538-2358:155:90:201
 Susan Dalsass:(206) 654-6279:250:60:50
 Archie McNichol:(206) 548-1348:250:100:175
 Jody Savage:(206) 548-1278:15:188:150
 Guy Quigley:(916) 343-6410:250:100:175
 Dan Savage:(406) 298-7744:450:300:275
 Nancy McNeil:(206) 548-1278:250:80:75
 John Goldenrod:(916) 348-4278:250:100:175
 Chet Main:(510) 548-5258:50:95:135
 Tom Savage:(408) 926-3456:250:168:200
 Elizabeth Stachelin:(916) 440-1763:175:75:300
[root@localhost sctript]# awk -F: 'BEGIN{printf "%-20s %-15s %-4s %-4s %-4s\n","Name","PHone","Jan","Feb","MAR"}BEGIN{print"--------------------------------------------------"}{printf "%-20s %-15s %-4s %-4s %-4s\n",$1,$2,$3,$4,$5}' awk01
Name                 PHone           Jan  Feb  MAR 
--------------------------------------------------
 Mike Harrington     (510) 548-1278  250  100  175 
 Christian Dobbins   (408) 538-2358  155  90   201 
 Susan Dalsass       (206) 654-6279  250  60   50  
 Archie McNichol     (206) 548-1348  250  100  175 
 Jody Savage         (206) 548-1278  15   188  150 
 Guy Quigley         (916) 343-6410  250  100  175 
 Dan Savage          (406) 298-7744  450  300  275 
 Nancy McNeil        (206) 548-1278  250  80   75  
 John Goldenrod      (916) 348-4278  250  100  175 
 Chet Main           (510) 548-5258  50   95   135 
 Tom Savage          (408) 926-3456  250  168  200 
 Elizabeth Stachelin (916) 440-1763  175  75   300 
2.删除行内与第一列字符相同的字符
[root@localhost sctript]# cat c
a b c a d a
s d d d x s a
h j s a s h j h
j d f j a s j k j
[root@localhost sctript]# awk '{a=$1;gsub(" ?"a,"");print a""$0}' c
a b c d
s d d d x a
h j s a s j
j d f a s k

a=$1把第一列赋值变量a。
gsub(" ?"a,"")用函数gsub进行行内全局替换," ?"a是正则,表示前面的空格可以没有也可以有一次,把匹配到的全部替换成空格。
print a""$0打印,在行首添加上变量a,也就字段$1。

3.打印学生的平均成绩
[root@localhost sctript]# cat d
Sophia huaxue 90
Faye   huaxue 80
Sophia wuli   70
Faye   wuli   60
[root@localhost sctript]# awk '{b[$1]=$1} {a[$1]++;c[$1]+=$3} END {for(i in b);for(i in a);for(i in c){y=c[i]/a[i];printf("%-8s平均成绩为:\t%2.2f\n",b[i],y)}}' d
Sophia  平均成绩为:  80.00
Faye    平均成绩为:  70.00

分析分两步:
1.先打印名字
awk '{b[$1]=$1}END{for(i in b)print b[i]}' d
Sophia
Faye
{b[$1]=$1}以名字为变量b的下标,并在里面赋值名字。
END{for(i in b)print b[i]}在最后,遍历数组b,并打印每一个元素。

2.打印平均分数
]# awk '{a[$1]++;c[$1]+=$3}END{for(i in a){y=c[i]/a[i];print y}}' d
80
70
{a[$1]++;c[$1]+=$3}以名字为变量a的下标,自增计数。以名字为变量c的下标,拿自己和$3分数的相加在赋值给自己。
{y=c[i]/a[i];print y}总分除以计数的到平均分

把两个整合awk '{b[$1]=$1} {a[$1]++;c[$1]+=$3} END {for(i in b);for(i in a);for(i in c){y=c[i]/a[i];printf("%-8s平均成绩为:\t%2.2f\n",b[i],y)}}' d

4.文本a记录姓名、学号,文本b记录学号、学科、成绩。把a和b组合成一张表格。
[root@localhost sctript]# cat a
Sophia|0001
Faye|0002

[root@localhost sctript]# cat b
0001|Chinese|77
0001|Music|90
0002|Chinese|62
0002|Music|80
[root@localhost sctript]# awk -F \| 'NR==FNR{a[$2]=$1;next}{printf "%-8s %-6s %-8s %-2s\n",a[$1],$1,$2,$3}' a b
Sophia   0001   Chinese  77
Sophia   0001   Music    90
Faye     0002   Chinese  62
Faye     0002   Music    80

NR表示a数据和b数据的总行数,FNR表示a和b分别计数
NR==FNR为真时表示读入的是a数据,执行之后的代码a[$2]=$1;next
a[$2]=$1;next表示把a文件切片的字段1放入数组a,数组a下标用字段2的值
NR==FNR为假时表示读入的是b数据,执行printf中的代码

printf "%-8s %-6s %-8s %-2s\n",a[$1],$1,$2,$3格式化输出b文件切片后的值
a[$1],$1,$2,$3,其中第一个a[$1]是读取a文件时新建的数组a,这是个关联数组,文件a中$2的值等于文件b中$1的值,所以就可以把文件a存入数组的值和文件b的各字段数值一起打印出来。

5.把文本分片后按字母顺序输出相同两个字符的字段
[root@localhost sctript]# cat sting 
asf|zz|123|bb|q23|cc|py|dd|ab|ee|x6|ff|ffq|gg|aaa|hh|aa
[root@localhost sctript]# awk -F \| '{for(i=1;i<=NF;i++){if($i~/^([a-z]){2}$/) {a=substr($i,1,1);b=substr($i,2,2);if(a==b) print $i | "sort"}}}' sting 
aa
bb
cc
dd
ee
ff
gg
hh
zz

文本切割后按照字段个数挨个循环,用模式匹配出只有2个字母的字段,对这个字段的两个字母对比,相等时打印并传递到sort命令中排序。

6.把grades中每行的字段分别从小到大排序
[root@localhost sctript]# cat grades 
44 55 66 22 77 99
100 22 77 99 33 66
55 66 100 99 88 45
[root@localhost sctript]# vim sorter.awk 
#!/bin/awk -f
#这里我用选择排序算法进行排序
{for(i=0;i<NF;i++){array[i]=$(i+1)}}
sort(array)

function sort (temp){
        for(i=0;i<length(temp)-1;i++){
                min=i
                for(j=i+1;j<length(temp);j++){
                        if(temp[min]>temp[j]){
                                min=j
                        }
                }
                if(min!=i){
                        temp1=temp[i]
                        temp[i]=temp[min]
                        temp[min]=temp1
                }
        }
        for(i=0;i<NF;i++){
                printf("%d ", temp[i])
        }
        printf "\n"
}
[root@localhost sctript]# ./sorter.awk grades
22 44 55 66 77 99 
22 33 66 77 99 100 
45 55 66 88 99 100

推荐阅读更多精彩内容

  • Linux指令中文说明传送入口 整理自Linux指令中文说明 文本和数据进行处理的编程语言awk 是一种编程语言,...
    释闲人阅读 1,618评论 1 6
  • awk介绍awk变量printf命令:实现格式化输出操作符awk patternawk actionawk数组aw...
    哈喽别样阅读 1,291评论 0 4
  • awk: grep,sed,awk grep:文本过滤 sed:文本编辑 awk:文本格式化工具; 1 什么是aw...
    木林森阅读 1,499评论 0 16
  • 本章主要学习内容awk介绍 awk基本用法 awk变量 awk格式化 awk操作符 awk条件判断 a...
    楠人帮阅读 1,071评论 0 8
  • 元旦放假三天,带着儿子去了佛山探望弟弟一家和妈妈。本来是因为爸爸跟弟弟他们回老家去搞房屋地基去了,妈妈一个人要带四...
    甜小丫的芳草园阅读 271评论 2 1
  • 前段时间很火的一封辞职信:“世界这么大,我想去看看”,当时这句话火遍网络,说出来很多热血青年们的心声。但最近开学季...
    吴金兰阅读 1,727评论 0 0
  • 为你我受冷风吹寂寞时候流眼泪 夜深人静不能睡昔日往事弹指挥 佳人远去心已碎浪子忧愁如山堆为情所困心好累可怜此时不能...
    柯西中值定理阅读 100评论 0 0
  • 首先,我给大家科普一下什么是「逼数」。 逼数 北方方言,「逼」是愤怒情绪的语气程度副词,表达很生气很呵呵的意思。 ...
    珊珊子阅读 925评论 0 0
  • 2016总结:升,生,尝试写和画。 2017计划:锻炼恢复,好好带娃,努力工作,继续写、画、读书。
    海蓝26阅读 225评论 2 3