Shell编程之AWK的使用

背景

AWK是一个优良的文本工具,是Linux环境下现有的功能最强大的数据处理引擎之一。AWK 提供了极其强大的功能:可以进行样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。他的创建者创建者已将它正式定义为“样式扫描和处理语言”。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

语法格式

不管语法怎么复杂,其语法始终为:

awk 'pattern {action}'

格式详解-pattern

pattern是执行条件,通常是正则表达式

条件类型 条件 条件说明
AWK保留字 BEGIN 在AWK程序开始时执行,仅会在程序开始的时候被执行一次
AWK保留字 END 在AWK程序结束之前执行,仅会在程序结束的时候执行一次
关系运算符 > 大于
关系运算符 < 小于
关系运算符 == 判断是否等于
关系运算符 >= 大于等于
关系运算符 <= 小于等于
关系运算符 != 不等于
关系运算符 pattern1~pattern2 判断pattern1是否包含能匹配上pattern2的字符串
关系运算符 pattern1!~pattern2 判断pattern1是否不包含能匹配上pattern2的字符串
正则判断 /pattern/ //之间为正则表达式,也支持字符串

格式详解-action

awk的动作通常为:格式化输出

内置变量

$n:表示第几个变量,如:$1,$2,就表示第一个变量,第二个变量
$0:表示执行过程中当前行的全部内容,即,所有的变量值
$NF:表示最后一个变量
NR:表示记录数,即当前执行的行号
FNR:同NR,这个是相对于文件而言的
NF:表示字段个数
FS:字段分隔符,默认是任何的空格
RS:记录分隔符,默认是一个换行符
OFS:输出字段分隔符,默认是一个空格
ORS:输出记录分隔符,默认是换行符
ARGC:命令行参数的数量
ARGV:命令行参数的数组
ARGIND:命令行当前文件的位置(从0开始)
CONVFMT:数字转换格式(默认为:%.6g)
OFMT:数字的输出格式(默认是:%.6g)
ENVIRON:环境变量关联数组
ERRNO:最后一个系统错误描述
FIELDWIDTHS:字段宽度列表(空格分隔)
FILENAME:当前输入的文件的名字
IGNORECASE:如果为真,就忽略大小写
RSTART:match函数匹配的字符串的第一个位置
RLENGTH:match函数匹配的字符串的长度
SUBSEP:数组下标分隔符(默认是34)

命令的使用

现在,先准备一个测试文件test.txt,文件内容如下:

//假设该文件保存的数据格式为
//名字 语文成绩 数学成绩 英语成绩 物理成绩 化学成绩
xiaoming 98 99 44 22 56
xiaohong 56 78 24 55 99
xiaodong 77 50 29 64 72

格式化输出:输出每个人对应的数学成绩,即,输出第一个和第三个字段

awk '{printf $1 "\t" $3 "\n"}' test.txt
#此处可以使用print代替printf,使用printf时,需要在后面加上\n,否则的话不会换行
#print会在输出后自动换行,所以不需要加。
#输出多个字符用空格隔开
#执行结果如下
root@localhost:/home/test/mi> awk '{printf $1 "\t" $3 "\n"}' test.txt
xiaoming 99
xiaohong 78
xiaodong 50

格式化输出:在上面的案例的输出结果之前,输出“This is math.”

awk 'BEGIN {print 'This is math.'} {printf $1 "\t" $3 "\n"}' test.txt
#执行结果如下
root@localhost:/home/test/mi> awk 'BEGIN {print 'This is math.'} {printf $1 "\t" $3 "\n"}' test.txt
This is math.
xiaoming 99
xiaohong 78
xiaodong 50

关系运算符的使用:查询数学成绩大于90的人

cat test.txt | grep -v name |awk '$3 > 90 {print $1}'
#判断$3>90成立后,才会执行后续的输出操作
#运行结果如下:
root@localhost:/home/test/mi> cat test.txt | grep -v name |awk '$3 > 90 {print $1}'
xiaoming

正则匹配:查找xiaodong的所有成绩

awk '/xiaodong/ {print}' test.txt
#//中表示要匹配的正则表达式
#运行结果如下:
root@localhost:/home/test/mi> awk '/xiaodong/ {print}' test.txt
xiaodong 77 50 29 64 72

操作字符串:输出a_nb_cidskf_frk_fds最后一个_后面的字符串

root@localhost:/home/test/mi> echo a_nb_cidskf_frk_fds | awk -F '_' '{print $NF}'
fds

特殊的输入输出语句

next语句
在逐行匹配的过程中,遇到next关键字,会跳过这一行,直接读取下一行
getline
输出重定向需用到getline函数。getline从标准输入、管道或者当前正在处理的文件之外的其他输入文件获得输入。它负责从输入获得下一行的内容,并给NF,NR和FNR等内建变量赋值。如果得到一条记录,getline函数返回1,如果到达文件的末尾就返回0,如果出现错误,例如打开文件失败,就返回-1
具体用法:

  • 当其左右无重定向符时|,<时:getline作用于当前文件,读入当前文件的第一行给其后跟的变量v或$0(无变量),由于awk在处理getline之前已经读入了一行,所以getline得到的返回结果是隔行的。
  • 当其左右有重定向符时|,<时:getline则作用于定向输入文件,此时该文件是刚打开,并没有被awk读入一行,只是getline读入,那么getline返回的是该文件的第一行,而不是隔行。

循环控制语句

  • for循环
    for(变量 in 数组)
    {代码块}
    for(变量;条件;表达式)
    {代码块}

  • while循环
    while(表达式)
    {代码块}

  • do … while循环
    do(代码块)
    while(表达式)
    关键字
    break:退出循环
    continue:退出本次循环,进入下一次循环
    next:读取下一行
    exit:退出循环进入end语句,如果没有end语句,就直接退出

AWK操作文件

  • 打开文件 open(“filename”)
  • 关闭文件 close(“filename”)
  • 重定向到另一个文件,如echo | awk ‘{printf(“hello word!n”) > “filename”}’

AWK的数组

awk的数组不用提前声明,也不用指定大小。数组的初始化用0或者空字符串来实现。具体的使用通过上下文来定。
数组的定义

awk 'BEGIN{
Array[1]="xiao"
Array[2]="mi"
Array["birth"]="1994"
Array["hobby"]="music"
}'

输出数组的内容

#有输出(默认无序)
for(item in Array)
{print Array[item]};

#乱序输出
#此处需要注意的是下标是从1开始
for(i=1;i<length(Array);i++)
{print Array[1]}

输出数组的长度

awk 'BEGIN{
str="this is test data for xiaomi
//通过split获取数组长度
lens=split(str,Array,"")
//通过length获取数组的长度
//需要注意的是,如果AWK的版本过低,是不支持length获取数组长度的
print length(Array),lens
}'

判断数组中是否存在某一指定的值
错误的做法
首先要注意的是,一旦出现数组的引用,AWK就会自动创建该元素,这是本次演示的错误原因

awk 'BEGIN{
     Array[1]="xiao"
     Array[2]="mi"
     Array["birth"]="1994"
     Array["hobby"]="music"
     //此处,会自动创建一个Array["home"]的引用,其值为空
     if(Array["home"]!="China"){
        print "not found home"
      };
     for(item in Array){
        print item,Array[item];
     }
}'
//此时的运行结果是:
not found home 
birth 1994
hobby music
home
1 xiao
2 mi
//可以发现此时虽然得到我们想要的结果了,但是数组本身发生了变化,多了一个元素

正确的做法

awk 'BEGIN{
     Array[1]="xiao"
     Array[2]="mi"
     Array["birth"]="1994"
     Array["hobby"]="music"
     if("home" in Array){
        print "There is home"
      };
     for(item in Array){
        print item,Array[item];
     }
}'
//此时的运行结果是
birth 1994
hobby music
1 xiao
2 mi

删除数组的某一元素

#会删除Array["hobby"]的元素值
delete Array["hobby"]

AWK的执行流程

  • 如果有BEGIN语句,即先执行BEGIN语句
  • 如果没有BEGIN语句,就读取第一行,把第一行的数据,按照顺序,依次赋值给$1,$2等
  • 如果有限定条件,根据条件是否执行后续语句
  • 读入下一行,重复执行上一条步骤,直到结束
  • 执行完所有的行,如果存在END语句,就执行,没有就结束

你可能感兴趣的:(shell相关知识)