awk
awk是一个报告生成器,它拥有强大的文本格式化能力,这是专业术语。
你可能不理解所谓的所谓的报告生成器中的“报告”是什么?你可以吧“报告”理解为报表或者表格,也就是说,我们可以利用awk命令,将一些文本整理成我们想要的样子,比如把一些文本整理成表的样子,然后再展示出来,刚才概念中提到的“文本格式化的能力”,也就是这个意思。如果还不理解,不用着急,当你看到后面的示例时,自然会明白awk所擅长的“文本格式化”能力是什么。
awk早期是在unix上实现的,所以我们现在在linux的所使用的awk其实是gawk,也就是GUN awk ,简称为gawk;awk还有一个版本,New awk , 简称为nawk, 但是linux中最常用的还是gawk。
grep、 sed 、awk 被称为linux中的“三剑客”
我们总结一下这三个“剑客”的特长
grep 更适合单纯的查找或匹配文本
sed 更适合编辑匹配到的文本
awk 更适合格式化文本,对文本进行较复杂格式处理
awk基础
awk基本语法如下,看不懂没关系,我们会慢慢举例。
awk [options] 'program' file1,file2,'''
对于上述语法中的program来说,又可细分为pattern和action,也就是说,awk的基本语法如下
awk [options] 'Pattern{Action}' file
从字面意思上理解,action指的就是动作,awk擅长文本格式化,并且将格式化以后的文本输出,所以awk最常用的动作就是print和printf,因为awk要把格式化完成后的文本输出啊,所以这2个动作最常用。
我们先从最简单的用法开始了解awk,先不适用[optioins],也不指定pattern, 直接适用最简单的action,从而开始认识awk,示例如下
上图中,我们只是适用awk执行了一个打印的动作,将aaa文件中的内容打印出来
现在 我们来操作另一个类似场景
上图中的awk '{print $5}' 表示输出df的信息的第5列,$5表示将当前行按照分隔符分割后的第5列,不指定分隔符时,默认适用空格作为分隔符,信心的你一定发现了,上图用的空格不止有一个,而是有连续多个空格,awk自动将连续的空格理解为一个分隔符了,是不是比cut命令要简单的多?
awk是逐行处理的,逐行处理的意思就是说,当awk处理一个文本是时,会一行一行进行处理,处理完当前行,再处理下一行,awk默认以“换行符”为标记,识别每一行,也就是说awk根我们人类一样,每次遇到“回车换行”,就人为是当前行的结束,新的一行开始,awk会按照用户指定的分隔符取分割当前行,如果没有指定分隔符,默认使用空格作为分隔符。
$0表示整行,$NF表示当前行分割后的最后一列($0和$NF均为内置变量)
注意,$NF和NF 的意思是不一样的,对于awk来说,$NF表示最后一个字段,NF表示当前行被分隔符切开以后,一共有几个字段。
也就是说,假如一行文本被空格分成了7段,那么NF的值就是7,$NF的值就是$7,而$7表示当前行的第7个字段,也就是最后一列,那么每行的倒数第二列可以写为$(NF-1)
我们也可以一次输出多列,使用逗号隔开要输出的多个列,如下,一次性输出第一列和第二列
[root@laobai ~#awk '{print $1,$2}' test abc 123 8ua 456
同理,也可以一次性输出多个指定的列,如下图
[root@laobai ~#awk '{print $1,$5}' test abc 8ua 7y7
我们发现,第一行没有第五列,所以并没有输出第五列的文本,而第二行有第五列,所以有输出
除了输出文本中的列,我们还能添加自己的字段,将 自己的字段与文件中的列结合
从上述实现中可以看出,awk 可以灵活的将我们指定的字符与每一列进行拼接,或者把指定的字符当做一个新列插入到原来的列中,也就是awk格式化文本能力的体现。
但是要注意,$1 这种内置变量的外侧不能加入双引号,否则$1会被 当做文本输出,如下图
我们也可以整行输出,如下两种发方法都表示输出整行
awk包含两种特殊模式:BEGIN和END
BEGIN模式指定了处理文本之前需要执行的操作
END模式指定了处理完所有行之后所需要执行的操作
BEGIN示例
如果我们想要awk先执行BEGIN模式指定的动作,再根据我们自定义的动作去操作文本,方法如下
聪明的你一定明白了,END模式就是在处理完所有的指定文本之后,需要指定的动作;那么我们可以结合BEGIN模式和END模式一起使用,示例
awk分隔符
(1) 逗号分隔符
(2) 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式
(3) 如省略item,相当于print $0
选项:
-F 指明输入时用到的字段分隔符
-v var=value: 自定义变量
[root@laobai ~#awk -F: '{print $1" "$2}' /etc/passwd root x bin x daemon x
-F: = -v FS="="
[root@laobai ~#awk -v FS=":" '{print $1,$3}' /etc/passwd root 0 bin 1 daemon 2
这2个命令时相同效果
示例
awk '{print "hello,awk"}' awk –F: '{print}' /etc/passwd awk –F: ‘{print “wang”}’ /etc/passwd awk –F: ‘{print $1}’ /etc/passwd awk –F: ‘{print $0}’ /etc/passwd awk –F: ‘{print $1” ”$3}’ /etc/passwd tail –3 /etc/fstab |awk ‘{print $2,$4}’
awk变量
变量:内置变量和自定义变量
FS:输入字段分隔符,默认为空白字符
awk -v FS=':' '{print $1,FS,$3}’ /etc/passwd awk –F: '{print $1,$3,$7}’ /etc/passwd
OFS:输出字段分隔符,默认为空白字符
awk -v FS=":" -vOFS=":" '{print $1,$3,$7}' /etc/passwd
RS: 输入记录分隔符,指定输入时的换行符
awk -v RS=" " '{print}' /etc/passwd
ORS:输出记录分隔符,输出时用指定符号代替换行符
awk -v RS=' ' -v ORS='###'‘{print }’ /etc/passwd
NF:字段数量(一行有多少列)
awk -F: ‘{print NF}’ /etc/fstab,引用内置变量不用$ awk -F: '{print $(NF-1)}' /etc/passwd
NR:记录号(也就是行号)
awk '{print NR}' /etc/fstab ; awk END'{print NR}' /etc/fstab
FNR: 各文件分别计数,记录号
awk '{print FNR}' /etc/fstab /etc/inittab
FILENAME: 当前文件名
awk '{print FILENAME}’ /etc/fstab
ARGC:命令行参数个数
awk '{print ARGC}’ /etc/fstab /etc/inittab awk ‘BEGIN {print ARGC}’ /etc/fstab /etc/inittab
ARGV:数组,保存的是命令行所给定的各参数
awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab awk ‘BEGIN {print ARGV[1]}’ /etc/fstab /etc/inittab
注意:在awk中,只有在引用$0,$1,$2等内置变量才会用到$符号,当在引用其他变量时,不管是内置变量还是自定义变量,都不适用$符,而是直接适用变量名
例
[root@laobai ~#df | awk '{print NR,$1}' 1 Filesystem 2 /dev/sda2 3 /dev/sda1 4 /dev/sda
自定义变量
自定义变量(区分字符大小写)
(1)-v var-value
(2)在program中 直接定义
示例
[root@laobai ~#awk -v var=myvar 'BEGIN{print var}' myvar
[root@laobai ~#awk 'BEGIN{myvar="ttt";print myvar}' ttt
也可以一次性定义多个变量
[root@laobai ~#awk 'BEGIN{myvar="111";yourvar="222";print myvar,yourvar}' 111 222
也可以在awk中引用shell 变量
[root@laobai ~#var=666 [root@laobai ~#awk -v myvar=$var 'BEGIN{print myvar}' 666
示例:
awk -v test='hello gawk' '{print test}' /etc/fstab awk -v test='hello gawk' 'BEGIN{print test}' awk 'BEGIN{test="hello,gawk";print test}' awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd cat awkscript {print script,$1,$2} awk -F: -f awkscript script=“awk” /etc/passwd
printf命令
print "FORMAT" ,item1,item2 .... 格式化输出命令
(1)必须指定FORMAT
(2)不会自动换行,需要显示给出换行控制符,
(3)FORMAT中需要分别为后面每个item指定格式符
格式符:与item一一对应
%c: 显示字符的ASCII码
%d, %i: 显示十进制整数
%e, %E:显示科学计数法数值
%f:显示为浮点数
%g, %G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%: 显示%自身
修饰符:
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
-: 左对齐(默认右对齐) %-15s
+:显示数值的正负符号 %+d
示例
awk -F: ‘{printf "%s",$1}’ /etc/passwd awk -F: ‘{printf "%s ",$1}’ /etc/passwd awk -F: '{printf "%-20s %10d ",$1,$3}' /etc/passwd awk -F: ‘{printf "Username: %s ",$1}’ /etc/passwd awk -F: ‘{printf “Username: %s,UID:%d ",$1,$3}’ /etc/passwd awk -F: ‘{printf "Username: %15s,UID:%d ",$1,$3}’ /etc/passwd awk -F: ‘{printf "Username: %-15s,UID:%d ",$1,$3}’ /etc/passwd
操作符
+ - * / ^ %(取模,也就是除 留下的余数),例如3取10的模 就是 1
-x 整数转换为负数
+x 转换为数值
赋值操作符
=,+=,-=,*=,/=,%=,^=, ++ , - -
比较操作符:
==,!=,>, >=, <, <=
模式匹配符
~: 左边的字符串是否能和右边的模式匹配
!~:左边的字符串是否能和右边的模式不匹配
示例
awk –F: '$0 ~ /root/{print $1}‘ /etc/passwd awk '$0~“^root"' /etc/passwd awk '$0 !~ /root/‘ /etc/passwd awk –F: ‘$3==0’ /etc/passwd
逻辑操作符
逻辑与&&, 逻辑或|| ,非 !
示例
awk –F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd awk -F: ‘!($3==0) {print $1}' /etc/passwd awk -F: ‘!($3>=500) {print $3}’ /etc/passwd 函数调用: function_name(argu1, argu2, ...)
条件表达式(三目表达式):
selector?if-true-expression:if-false-expression
示例:
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s ",$1,usertype}' /etc/passwd
awk PATTERN
PATTERN:根据pattern条件,过滤匹配的行,再做处理
(1):空模式,匹配每一行
(2):/ 正则表达式/,仅能处理正则表达式匹配到的行,需要用/ /括起来
(3):关系表达式,结果为“真”(非0或非空字符串),才会被处理;“假”(0或空字符串)则不处理
(4):地址定界,支持PATTERN和条件表达式;不支持数字直接定界
line ranges:行范围(从A行到B行之间的内容)
示例
awk -F: ‘/^root>/,/^nobody>/{print $1}' /etc/passwd awk -F: ‘(NR>=10&&NR<=20){print NR,$1}' /etc/passwd
(5):BEGIN模式,BEGIN{}:仅在文本处理之前执行;END{}:仅在文本处理完成之后执行
示例
awk -F: 'i=1;j=1{print i,j}' /etc/passwd awk –F: '$3>=1000{print $1,$3}' /etc/passwd awk -F: '$3<1000{print $1,$3}' /etc/passwd awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
[root@laobai ~#awk -F: 'BEGIN {print "USER USERID"}{print $1":"$3} END{print "end file"}' /etc/passwd [root@laobai ~#awk -F : '{print "USER USERID";print $1":"$3} END{print "end file"}' /etc/passwd [root@laobai ~#awk -F: 'BEGIN{print " USER UID --------------- "}{print $1,$3}' /etc/passwd
seq 10 |awk ‘i=0’
seq 10 |awk ‘i=1’
seq 10 | awk 'i=!i‘
seq 10 | awk '{i=!i;print i}‘
seq 10 | awk '!(i=!i)'
seq 10 |awk -v i=1 'i=!i'
将passwd里 UUID大于500的值取出来,并以 USER USERID开头 且对齐
控制语句
(1)if-else 语法:if(condition) statement [else statement]
awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd awk -F: '{if($3>=1000) {printf "Common user: %s ",$1} else {printf "root or Sysuser: %s ",$1}}' /etc/passwd
(2)while 语法:while(condition) statement 条件真进入循环,条件假退出循环
awk ‘/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=10) {print $i,length($i)}; i++}}’ /etc/grub2.cfg
(3)do-while 语法:do statement while(condittion) 先执行一次do,然后判断是否进入循环
[root@laobai ~#awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'
5050
(4)for 语法:for(variable assignment; condition; iteration process){for-body} *特殊用法:能够遍历数组中的元素,for(var in array) {for-body}
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
(5)switch 语法:switch(expression) {case VALUE1 or /PEGEXP/: statement; case VALUE2 or /PEGEXP2/: statement; ... ;default: statement}
switch 后面跟的变量 被 后面的case 匹配到 则执行
(6)next:控制awk的内生循环,提前结束对本行的处理直接进入下一行
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
(7)continue | break | exit:退出循环,和shell中用法相同
continue 是结束当前循环,下一个循环继续运行
break 是结束循环
awk数组
关联数组:array [index-expression]
index - expression:
(1)可使用任意字符串;字符串要使用双引号括起来
(2)如果 某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
(3)若要判断数组中是否存在某元素,要使用“index in array” 格式进行遍历