Linux学习64 awk使用与实战
一、回顾:bash脚本编程数组
1、数组,字符串处理
2、数组:
a、数组:declare -a
index:0-
b、关联数组:declare -A
3、字符串处理
a、切片,查找替换,查找删除,变量赋值
二、GUN awk
1、文本处理三剑客:grep,sed,awk
a、grep,egrep,fgrep:文本过滤工具:pattern
b、sed:行编辑器
(1)、模式空间,保持空间
c、awk:报告生成器,格式化文本输出
d、AWK:Aho,Weinberger,Kernighan,三个人对应三个字母
AWK --> New AWK,NAWK
GAWK --> GUN AWK
[root@www application]# which awk /usr/bin/awk [root@www application]# ls -l /usr/bin/awk lrwxrwxrwx. 1 root root 4 6月 4 2019 /usr/bin/awk -> gawk
2、awk
a、pattern scanning and processing language(模式扫描及实现处理的语言)
b、基本用法:gawk [options] 'program' FILE ...
(1)、program: PATTERN{ACTION STATEMENTS}
语句之间用分号分隔
print,printf
(2)、选项
-F:指明输入时用到的字段分隔符;
-v var=value:自定义变量
3、awk的使用
a、我们接下来说一说awk是怎么处理文本的,sed是读取文本到模式空间中在模式空间进行处理,这种实现方式就是实现文本编辑的。我们awk也是从我们对应的文件中一次读取一行文本,这个读取出来的文本怎么处理呢?在读取出来的这一行文本后他首先会将这个文本按照我们的输入分隔符进行切片,把整个行文本按照我们此前指明的输入分隔符切片成N个组成部分,比如默认分隔符是空白字符,那么他就会切割一行文本为N片,并且把每一片在awk内部自动赋予一个内建的变量来进行保存,这个内建变量叫$1,$2,$3....一直到最后一行,awk可以自行你有多少个字段他就有多少个内建变量来保存这些数据。我们可以在切片后然后显示某一段,如果要显示整个内容我们可以用$0来表示。很显然切割成片以后我们还可以对每一片做额外的加工和处理。当我们对文件中的每一行做了加工切片以后,我们还可以额外做一些条件性的过滤。
(1)、比如我们切片以后就在每一行的第二片判断它是否在某一个数值范围内,如果在某一数字范围内,我们就把这一行的第四个字段显示出来,这就是条件判断的应用
(2)、我们还可以对这一行中的每一个片段挨个做类似加工,即我们可以对这一行的每一个片段做循环,然后对每一个片段做加工处理,注意,awk的循环功能不是在行间循环的,因为awk本身就可以遍历文件。但是awk依然有循环的功能,主要是在字段间完成遍历操作的,因为一个行有n个字段,我们要逐字段处理的话我们就必须在字段间完成循环机制才行。
b、相应的使用方式
(1)、基本用法
[root@www ~]# tail -3 /etc/fstab |awk '{print $1,$4}' /dev/mapper/centos-root defaults UUID=fd514a18-84be-4460-8130-706b3ec88673 defaults /dev/mapper/centos-swap defaults
4、awk用法
a、常用输出命令
(1)、print: 使用方式为 print item1,item2
要点:
1)、逗号分隔符
2)、输出的各item可以是字符串,也可以是数值,当前记录的字段,变量或awk的表达式
[root@www ~]# tail -3 /etc/fstab |awk '{print "hello",$1,$4,6}' hello /dev/mapper/centos-root defaults 6 hello UUID=fd514a18-84be-4460-8130-706b3ec88673 defaults 6 hello /dev/mapper/centos-swap defaults 6
3)、如果省略item,相当于print $0,即打印整行字符
b、变量
(1)、内建变量
1)、FS:input field seperator(输入字段分隔符),即我们以哪个字段做分隔符,默认为空白字符,如果我们要改变其值,我们只需要在命令行中使用-v选项指明变量名给其赋一个新值就可以。或者也可以使用-F选项效果也是一样的
[root@www ~]# awk -v FS=':' '{print $1}' /etc/passwd |tail -2 tss geoclue
[root@www ~]# awk -F ':' '{print $1}' /etc/passwd |tail -2 tss geoclue
2)、OFS:output field seperator(输出字段分隔符),即输出的字段用什么分隔,默认为空白字符,如果我们要改变其值,我们只需要在命令行中使用-v选项指明变量名给其赋一个新值就可以
[root@www ~]# awk -v FS=':' -v OFS='#' '{print $1,$2,$3}' /etc/passwd|tail -2 tss#x#59 geoclue#x#994
3)、RS:input record seperator,输入的换行符。现在我们把空格作为换行符,即某一行中有空白的话空白后就作为新行出现
[root@www ~]# awk -v RS=' ' '{print $1}' /etc/passwd
4)、ORS:output record seperator,输出时的换行符
[root@www ~]# awk -v ORS='#' '{print $1}' /etc/passwd
5)、NF:number of field:字段数量,自动保存在一个叫NF的内建变量中。比如我们现在要打印最后四行的字段数量
[root@www ~]# awk '{print NF}' /etc/passwd |tail -4 1 1 11 3
如果要打印最后一行的字段,可以使用$NF
[root@www ~]# awk -v FS=':' '{print $NF}' /etc/passwd |tail -4 /bin/bash /bin/bash /sbin/nologin /sbin/nologin
6)、NR:number of record,行数;即可以显示文件有多少行
[root@www ~]# awk '{print NR}' /etc/passwd 1 2 3 4 ...
7)、FNR:file number of record,各文件分别计数。如果用NR变量后面跟多个文件名则会统一计数,即所有行号一起统计,如果用FNR变量后面跟多个文件则第一个文件行号统计完了又开始统计第二个文件的行号
[root@www ~]# awk '{print FNR}' /etc/fstab /etc/issue 1 2 3 4 5 6 7 8 9 10 11 1 2 3
8)、FILENAME:当前正在处理的文件的文件名,如果我们有多行的话那么每一个行都会显示一次文件名
[root@www ~]# awk '{print FILENAME}' /etc/issue /etc/issue /etc/issue /etc/issue
9)、ARGC:命令行参数的个数
[root@www ~]# awk '{print ARGC}' /etc/issue /etc/fstab 3 3 3 3 3 3 3 3 3 3 3 3 3 3
也可以使用BEGIN显示一行
[root@www ~]# awk 'BEGIN {print ARGC}' /etc/issue /etc/fstab 3
10)、ARGV:数组,保存的是命令行所给定的各参数
[root@www ~]# awk 'BEGIN {print ARGV[0]}' /etc/issue /etc/fstab awk [root@www ~]# awk 'BEGIN {print ARGV[1]}' /etc/issue /etc/fstab /etc/issue [root@www ~]# awk 'BEGIN {print ARGV[2]}' /etc/issue /etc/fstab /etc/fstab
(2)、自定义变量(如果不对文件做处理直接使用BEGIN模式即可)
1)、-v var=value
变量名区分字符大小写。
[root@www ~]# awk -v test='hello gawk' 'BEGIN {print test}' hello gawk
2)、在program中直接定义
[root@www ~]# awk 'BEGIN {test="hello gawk";print test}' hello gawk
c、printf命令:格式化输出命令 printf FORMAT,item1,item2,...
(1)、FORMAT必须给出
(2)、不会自动换行,需要显示给出换行控制符,\n
(3)、FORMAT中需要分别为后面的每个item指定一个格式化符合
(4)、格式符
%c:显示字符的ASCII码
%d,%i:显示十进制整数
%e,%E:科学计数法数值显示
%f:显示为浮点数
%g,%G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%:显示%自身
现在我们来显示我们/etc/passwd中的每一行的用户
[root@www ~]# awk -F: '{printf "%s",$1}' /etc/passwd rootbindaemonadmlpsyncshutdownhaltmailoperatorgamesftpnobodysystemd-networkdbuspolkitdapachelibstoragemgmtabrtrpcpostfixntpchronysshdtcpdumptestusernginxredistomcatcentosgentooopenstackmogi lefsarchlinuxmoosefsmytestnovauser1user3bashbashernologintestbashuser2zhangsantssgeoclue[root@www ~]#
他的意思是将我们的$1套到%s上以字符串形式进行显示,并且默认不会加换行符,如果我们要加控制符则需要使用\n
[root@www ~]# awk -F: '{printf "%s\n",$1}' /etc/passwd root bin daemon ....
我们还可以加相应的字符串
[root@www ~]# awk -F: '{printf "username: %s\n",$1}' /etc/passwd username: root username: bin username: daemon ...
[root@www ~]# awk -F: '{printf "username: %s,UID: %d\n",$1,$3}' /etc/passwd username: root,UID: 0 username: bin,UID: 1 username: daemon,UID: 2 ...
(5)、修饰符
#[.#]:第一个数字用来控制显示的宽度,第二个数字表示小数点后的精度,比如%3.1f
现在我们将第一个参数固定15个字符的宽度(默认会右对齐)
[root@www ~]# awk -F: '{printf "username: %15s,UID: %d\n",$1,$3}' /etc/passwd username: root,UID: 0 username: bin,UID: 1 username: daemon,UID: 2 ...
我们也可以让其左对齐显示,只需要写成%-15s即可
[root@www ~]# awk -F: '{printf "username: %-15s,UID: %d\n",$1,$3}' /etc/passwd username: root ,UID: 0 username: bin ,UID: 1 username: daemon ,UID: 2 ...
我们也可以使用+显示数值的符号,即正负号,比如%+15s
d、操作符
(1)、算术操作符
x+y,x-y,x*y,x/y,x^y,x%y
-x
+x:转换为数值
(2)、字符串操作符:没有符号的操作符,表示字符串连接
(3)、赋值操作符
=,+=,-=,*=,/=,%=,^=
(4)、比较操作符
>,>=,<,<=,!=,==
(5)、模式匹配符
~
!~
(6)、逻辑操作符
&&
||
!
(7)、函数调用
function_name(argu1,argu2,...)
(8)、条件表达式:
selector?if-true-expression:if-false-expression(selector这个条件表达式为真则执行if-true-expression这个语句,如果为假则执行if-false-expression这个语句)
[root@www ~]# awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd root:Sysadmin or SysUser bin:Sysadmin or SysUser daemon:Sysadmin or SysUser
e、PATTERN:我们说过在awk用法gawk [options] 'program' FILE ...中 program是由PATTERN{ACTION STATEMENTS}组成,我们现在来介绍一下PATTERN。他可以理解为类似的地址定界符
(1)、empty(空):空模式,匹配每一行
(2)、/regular expression/:仅处理能够被此处的模式匹配到的行
我们来处理UUID的行
[root@www ~]# awk '/^UUID/{print $1}' /etc/fstab UUID=fd514a18-84be-4460-8130-706b3ec88673
我们来处理UUID以外的行
[root@www ~]# awk '!/^UUID/{print $1}' /etc/fstab
(3)、relational expression:关系表达式:结果有“真”有“假”;结果为“真”才会被处理
1)、“真”:结果为非0值,非空字符串
[root@www ~]# awk -F: '$3>1000{print $1,$3}' /etc/passwd testuser 5000 centos 5002 gentoo 5003 openstack 3000 mogilefs 5004 archlinux 5005 moosefs 5006 mytest 5007 nova 5009 user1 5010 user3 5011 bash 5012 basher 5013 nologin 5015 testbash 5016 user2 5017 zhangsan 5018
显示其默认shell为bash的用户
[root@www ~]# awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd root /bin/bash centos /bin/bash gentoo /bin/bash openstack /bin/bash mogilefs /bin/bash archlinux /bin/bash moosefs /bin/bash mytest /bin/bash bash /bin/bash basher /bin/bash testbash /bin/bash user2 /bin/bash zhangsan /bin/bash
我们也可以通过模式匹配来实现,即只要最后一个字段是以bash结尾的就将其处理
[root@www ~]# awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd root /bin/bash centos /bin/bash gentoo /bin/bash openstack /bin/bash mogilefs /bin/bash archlinux /bin/bash moosefs /bin/bash mytest /bin/bash bash /bin/bash basher /bin/bash testbash /bin/bash user2 /bin/bash zhangsan /bin/bash
(4)、line ranges:行范围
1)、startline,endline(awk不支持直接给出数字的格式),要基于行模式进行定界的话需要基于NR来判断
[root@www ~]# awk -F: '(NR>=2&&NR<=5){print $1}' /etc/passwd bin daemon adm lp
2)、/pat1/,/pat2/
[root@www ~]# awk -F: '/^root/,/^sync/{print $1,$3}' /etc/passwd root 0 bin 1 daemon 2 adm 3 lp 4 sync 5
(5)、BEGIN/END模式。awk有一种内部功能,就是他能自动遍历文件的每一行然后对每一行我们做出处理,而如果恰好我们的action没有对行内容做出任何处理,对于每一行他也会显示一个字符串出来,如果我们期望在处理文件开始之前就做一次操作,等文件所有的行都处理结束了再做一次操作就可以使用BEGIN/END模式。
1)、BEGIN{}:仅在开始处理文件中的文件之前执行一次
我们现在来显示一个用户的用户名和ID号加表头进行显示
[root@www ~]# awk -F: 'BEGIN{print "username uid \n-------------------"}' username uid ------------------- [root@www ~]# awk -F: 'BEGIN{print "username uid \n-------------------"}{print $1,$3}' /etc/passwd username uid ------------------- root 0 bin 1 daemon 2 ...
2)、END{}:仅在文本处理完成之后命令结束之前执行一次
[root@www ~]# awk -F: 'BEGIN{print "username uid \n-------------------"}{print $1,$3}END{print "====================\n end"}' /etc/passwd username uid ------------------- root 0 bin 1 daemon 2 ... ==================== end
f、常用的action
(1)、Expressions,比如谁大于谁谁小于谁
(2)、Control statements(控制语句):if,while等
(3)、Compound statements:组合语句
(4)、input statements:输入语句
(5)、output statements:输出语句
g、控制语句
if(condition){statements}
if(condition){statements} else {statements}
while(condition){statements}
do {statements} while(condition)
for(expr1;expr2;expr3){statements}
break
continue
delete array[index]
delete array
exit
{ statements }