七.Linux三剑客之awk命令

awk的调用方式与sed类似,脚本文件以#!/bin/awk -f或#!/bin/gawk -f开头。相比于grep和sed,awk正则表达式支持”?”和”+”两个拓展元字符。

  • awk模式匹配
$ cat file 
qwer


1234
[jin1ming@ML linux_shell]$ awk '/^$/{print "Hello World!"}' file 
Hello World!
Hello World!

^$在此处用于匹配空行

  • 记录和域
    awk认为每行为一个记录,行中每个字符串为域,域之间用空格,Tab键或其他符号进行分隔,分隔域的是分隔符。(分隔符默认为空格)
$ cat phoneinfo 
张三 湖南 123455
李四 山西 222222
王五 河南 333333
$ awk '{print $1}' phoneinfo 
张三
李四
王五
$ awk '{print $1;{print $0}}' phoneinfo 
张三
张三 湖南 123455
李四
李四 山西 222222
王五
王五 河南 333333
$ awk 'BEGIN{num=3} {print$num}' phoneinfo 
123455
222222
333333

《七.Linux三剑客之awk命令》0代表所有域
awk处理时,将进行逐行扫描
BEGIN在遍历文件前执行
awk中可使用变量

$ awk 'BEGIN{OFS=";"} {print $1,$2,$3}' phoneinfo | 
> awk 'BEGIN{FS=";";OFS="::"} {print $1,$2,$3}'
张三::湖南::123455
李四::山西::222222
王五::河南::333333

OFS为输出域分隔符,FS为(输入)域分隔符
该demo,将文件中的空格作为域分隔符进行域划分,
将“;”作为域分隔符进行输出到管道,
最后将管道中的文本按“;”作为域分隔符进行域划分,
最后将“::”作为输出域分隔符进行输出。

#修改字符串之间的空格为两个
$ cat phoneinfo 
张三  湖南  123455
李四  山西  222222
王五  河南  333333
$ awk 'BEGIN{OFS=","}{print $1,$2,$3}' phoneinfo 
张三,湖南,123455
李四,山西,222222
王五,河南,333333

说明空格作为域分隔符时,空格可为1个或多个

$ awk 'BEGIN{OFS="::"}{print $1,$2,$3}' phoneinfo |
> awk 'BEGIN{FS=":";OFS=","} {print $1,$2,$3}' 
张三,,湖南
李四,,山西
王五,,河南
$ awk 'BEGIN{OFS="::"}{print $1,$2,$3}' phoneinfo |
> awk 'BEGIN{FS="::";OFS=","} {print $1,$2,$3}' 
张三,湖南,123455
李四,山西,222222
王五,河南,333333

其他符号作为域分隔符只代表其一个字符,若想多个,必须使用“+”,
例如 \t+代表一个或多个该字符(支持正则)
注意此处第一个命令,输出中第二个域为空域

$ awk 'BEGIN{OFS="::"}{print $1,$2,$3}' phoneinfo |
> awk 'BEGIN{FS="::";OFS=",";ORS=";"}
> {print $1,$2,$3}' 
张三,湖南,123455;李四,山西,222222;王五,河南,333333;

ORS输出域分隔符,RS记录分隔符(默认都为换行符)

  • 关系和布尔运算符

需注意:
1:~ 匹配正则表达式
2: !~ 不匹配正则表达式
其他运算符与c语言一致

$ awk 'BEGIN{FS=":"} $2~/dd/' 2.txt 
2:dd
:ddddddd
  • 表达式
    统计行数
$ awk 'BEGIN{i=0} {print ++i}' 2.txt 
1
2
3
4
5

计算每人平均分数

$ cat score 
张三:78 92 87
李四:91 64 87
王五:34 99 56
$ cat awkdomo.awk 
#!/bin/awk -f

BEGIN {FS="[: ]"}
{sum=$2+$3+$4
avg=sum/3
print $1,":",avg}

$ chmod u+x awkdomo.awk 
$ ./awkdomo.awk score 
张三 : 85.6667
李四 : 80.6667
王五 : 63
  • 常用系统变量
    以上提到的不再重复

ARGC 命令行参数的数量
ARGIND 命令行中当前文件的位置(以0开始标号)
ARGV 命令行参数的数组(ARGV[0]存储的是执行脚本的程序名)
FILENAME 当前文件名
NF 当前记录中的域数量

$ cat awkdomo.awk 
#!/bin/awk -f

BEGIN{FS="[: ]"}

{print "\nARGC:",$ARGC,
    "\nARGIND:",$ARGIND,
    "\nARGV",$ARGV[2],
    "\nFILENAME:",$FILENAME,
    "\nNF:",$NF
}

$ ./awkdomo.awk score 

ARGC: 78 
ARGIND: 张三 
ARGV 张三:78 92 87 
FILENAME: 张三:78 92 87 
NF: 87

ARGC: 91 
ARGIND: 李四 
ARGV 李四:91 64 87 
FILENAME: 李四:91 64 87 
NF: 87

ARGC: 34 
ARGIND: 王五 
ARGV 王五:34 99 56 
FILENAME: 王五:34 99 56 
NF: 56
  • 格式化输出
    printf (格式控制符,参数) ,与c语言一致
  • 内置字符串函数

gsub(r,s) 在输入文件中用s替换r(全局替换)
gsub(r,s,t) 在t行中用s替换r
index(s,t) 返回s中字符串第一个t的位置
length(s) 返回s的长度
match(s,t) 测试s是否包含匹配t的字符串
split(r,s,t) 以t为分隔符将r进行分割,保存到数组s
sub(r,s,t) 将t中第一次出现的r替换为s
substr(r,s) 返回字符串r中从s开始的后缀部分
substr(r,s,t) 返回字符串r中从s开始长度为t的后缀部分

  • 向awk脚本传递参数
    awk -f 脚本文件 parameter=value 输入文件

    awk [awk命令] parameter=value 输入文件
$ awk '
BEGIN{n = 999; print n}
{if (n==1) print "Continue!"
} ' n=1 2.txt
999
Continue!
Continue!
Continue!
Continue!
Continue!

参数赋值在BEGIN后执行

  • 条件语句和循环语句
    和c语言完全一致,在判断时可以使用~匹配符和正则表达式作为if语句的条件
  • 数组
    awk数组的形式和c语言一致,只是无需定义就可以使用,需要注意的是数组下标不是必须为整数,可以为浮点数或字符串,而且09和9作为下标是不一样的
$ awk '
BEGIN{nums[1.5]=5.1} {print nums[1.5];print nums[1]} 
' score
5.1

5.1

5.1

$ awk '
BEGIN{nums[wer]=9;nums[2]=2;nums[9]=9} 
END {for (var in nums)
print var,":",nums[var]
}' score
 : 9
9 : 9
2 : 2

数组和参数相结合的根据姓名查询个人信息

$ cat awkdomo2.awk 
#!/bin/awk -f

BEGIN{
   if(ARGC>2){
       name=ARGV[1];
       delete ARGV[1] }
   else{
       while(!name){
           print "请输入姓名:";
           getline name< "-"}
       }
   }

   $1~name {print $1,$2,$3}
./awkdomo2.awk  张三 phoneinfo 
张三 湖南 123455

输入姓名使用read将会使得”请输入姓名:”疯狂输出,原因还尚未琢磨清楚

    原文作者:靳一鸣
    原文地址: https://www.jianshu.com/p/465180065690
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞