awk作为和sed、grep同级的文本处理命令,也又强大的文本分析功能,同样,它的原理并不困难,但操作很多且很杂,可以通过不同的需求进行自定义搭配。
awk工作原理
awk和另外两个命令的工作原理又不相同,当用户输入命令时,awk从标准输入或输入文件、命令行参数中读取数据,按照用户定义的模式进行匹配,匹配成功则执行相应操作,然后将结果输出到标准输出或指定的文件中
awk 提供了三个特殊的模式,BEGIN、pattern、END
BEGIN 块在任何输入数据处理之前执行,通常用于初始化变量、设置字段分隔符、打印头信息等
pattern 块在每条记录被读取后执行,如果记录与模式匹配,则执行相应的动作
END 块在所有输入数据处理完毕后执行,通常用于打印汇总信息、关闭文件等
所以,我们可以将awk工作的流程简单地分为三个步骤:
读:从文件、管道、标准输入等中读入一行然后将其放入到内存中
执行:根据命令按序执行,默认是处理每一行,可指定需求
重复:处理完当前记录后,awk继续读取下一条记录,并重复执行上述步骤,直到所有记录都被处理完
命令格式
原理只是辅助理解,最重要的还是能灵活使用命令
awk 的命令格式是
awk [选项] 模式或条件{操作} 文件
awk -f 脚本文件 文件
awk 关键字 选项 命令部分 '{xxxx}' 文件名
awk 支持内置变量和自定义变量
常见的内置变量有
FS:指定字段的分隔符,默认为空格或制表符
NF:当前正在处理的行的字段数,有的时候也用作表示列的个数
NR:当前处理的行的行号
$0:当前处理的行的整行
$n:当前处理行的第n个字段
FNR:各文件分别计数的行号
OFS:输出字段分隔符
ORS:输出记录分隔符
RS:记录行分隔符
案例
首先来点简单的,我们打印出第一列,就用/etc/passwd文件吧,将他拷贝到/opt目录下,方便操作,又不会对原文件产生影响
awk -F : '{print $1}' passwd -F 指定分隔符是必须要的,不指定分隔符啥也打不出来,
passwd文件每列之间的分隔符也确实 ":"
按照规则来,便能输出你想要的结果
同时打印一二两列,并且中间用 - - 隔开 ↓
如果中间要间隔一个制表符也是可以的,将 - - 换成 \t
有的时候,一个文件里并不只是以一个符号隔开的,可能前面是: 后面还穿插着/等之类的其他分隔符,这时候我们进行筛选的时候就需要设定两个分隔符
awk -F [:/] '{print $5 "- -"$8}' passwd 还以passwd文件为例,假如有另外一个分隔符/
照我们这样的分法,第一行打印出来的应该是 root 和空白,因为第八列恰好在 : 和 / 之间
我们来验证结果
事实亦是如此
简单的打印操作差不多就是这样,接下来我们要用到上面说到的那些什么 FS NR NF等内置变量
打印出含有数字的行的第一列,并显示行号
awk -F: '/[0-9]/{print NR"--"$1}' passwd NR显示行号,和实际内容之间也是用--隔开了
结果验证无误,若想要打印出含有数字的行的这一整行,我们就不需要特别指定$几列,改成$0即可,如果什么都不加,那将会只打印行号
接着,我现在想看看,passwd文件里每一行究竟有多少列,那假如 / 也算分隔符,那又有多少列?
awk -F: '{print NF}' passwd awk -F[:/] '{print NF}' passwd
看看前几行就差不多了,就不截全了,可以看到,每行有多少列被统计了出来,因为passwd本身并没有以/为分隔符,所以第二个命令打印出来的数字参差不齐,因为路径不一样
如果嫌麻烦不想查看有多少列,就想直接打印最后一列,那么我们的列号就直接改成 $NF就可以了
行号也可以指定,比如打印第一行第一列
awk -F: 'NR==1 {print $1}' passwd
应用到实际案例中
还是以网卡为例,要求直接输出 本机的ip地址是:
ifconfig ens33 | awk '/netmask/{print "本机的IP地址是:"$2}'
因为我们首先要筛选出含有ip地址的那一行(独有netmask的那一行,其实也可以根据自己的理解设定其他筛选条件),随后再输出ip地址所在的列
awk也可以像sed 还有 grep 那样,匹配查询一些特定的字符,比如,查找含有 root 的行
awk -F: '/root/' passwd
awk -F: '$1~/ro/' /etc/passwd 模糊查询,查询第一列含有ro的行
如果想要查找不含ro的行,就在 ~ 号前面加一个!表示不包含
我们还可以通过用字符串来实现查找功能,只不过这个查找就是比较精确的了,输入什么字符就精确匹配什么字符,比如查找最后一列是/bin/bash的
awk -F: '$7=="/bin/bash"' passwd 一定要加双引号,格式要正确,这样才能成功查找
我也是输了很多次发现格式有问题,最终加了双引号,加了/ 才查找成功
既然 = 号都出现了,那么 > 或者 < 应该也不意外,用法也如它们本身的含义一样,比如passwd文件里,第三列基本上都是数字,那我们就可以查找,第三列大于1000的行
不仅如此,逻辑符号也可以一并加入使用,第三列大于1000,并且小于1005
两条符合查询条件的结果被输出了出来,当然不仅可以只对数字的大小做要求,字符类型其他列等,都可以加入查询条件,具体按照什么规则来查询,就看自己的发挥了
awk同样可以选择输出偶数行还是奇数行 ,getline 读取数据时,得到的是下一行
seq 10 | awk '{getline;print $0}' 显示偶数行
seq 10 | awk '{print $0;getline}' 显示奇数行
前面提到FS、OFS、FNR 等内置变量,其实理解起来也非常简单
FS:指定字段的分隔符,默认为空格或制表符
如,我们要在在打印之前定义字段分隔符为冒号,打印第三行
awk 'BEGIN{FS=":"}{print $3}' passwd 这里BEGIN 就是在打印前做
FNR:追加行号
awk '{print FNR,$0}' passwd 就可以看到输出的内容的行号
OFS:输出字段分隔符
awk 'BEGIN{FS=":";OFS="- -"}' 这个结果的输出命令就和之前
awk -F [:/] '{print $5 "- -"$8}' passwd 输出效果一样,我就不演示了
ORS:输出记录分隔符
awk 'BEGIN{ORS=" "}{print $0}' passwd
把多行合并成一行 输出,输出的时候自定义以空格分隔每行,默认是回车键
RS:记录行分隔符
awk 'BEGIN{RS=":"}{print $0}' /etc/passwd
指定以什么为换行符,这里指定是冒号,指定的肯定是原文里存在的字符
awk 拥有一个特殊的功能,那就是可以加入条件判断语句或者循环语句等
awk if语句
还是passwd文件,比如,如果第三列大于1000,那就打印第三列,否则打印第一列
awk -F: '{if($3>1000){print $3}else{print $1}}' passwd
因为文件里的行数太多,我就不截全了,逻辑上来说,排除用户名就是数字的,输出结果中,应该全是大于1000的数字,如果不是数字,那就都是passwd文件中的用户名
awk数组
看到数组会觉得头疼吗?还好,awk里的数组目前用不到十分困难的,我们只需要会如何定义如何输出就好了
awk 'BEGIN{a[0]=10;a[1]=20;print a[1]}' 定义了a[0] a[1] 并打印a[1]
相应的,打印a[0] 输出结果肯定就是10了
awk 'BEGIN{a["abc"]=10;a["xyz"]=20;print a["abc"]}' 和上面一个意思
awk循环
上面的数组定义完,我们就可以插入循环啦
awk 'BEGIN{a[0]=10;a[1]=20;a[2]=30;for(i in a){print i,a[i]}}' 定义一个数组,再让i对应数组元素,并进行打印
awk '{line[NR] = $1} END{for(i in line){print "Line " i ":" line[i]}}' xslx.txt
将每一行的第一个字段存储在数组 line 中,使用行号 NR 作为索引,然后用for循环遍历数组,并打印每个元素及其索引
awk 的用法基本上就这么多,然后至此文本编辑三剑客的讲解也就圆满结束了,功能很多,所以东西也很多很杂,多敲多练熟稔于心