Linux九阴真经之无影剑残卷14(文本三剑客之awk)

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"\t"$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”\t”$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)不会自动换行,需要显示给出换行控制符,\n

(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\n",$1}’ /etc/passwd
awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F: ‘{printf "Username: %s\n",$1}’ /etc/passwd
awk -F: ‘{printf “Username: %s,UID:%d\n",$1,$3}’ /etc/passwd
awk -F: ‘{printf "Username: %15s,UID:%d\n",$1,$3}’ /etc/passwd
awk -F: ‘{printf "Username: %-15s,UID:%d\n",$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\n",$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 \n--------------- "}{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\n",$1} else {printf "root or Sysuser: %s\n",$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” 格式进行遍历

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2018-05-16 22:29  老白&  阅读(222)  评论(0)    收藏  举报