1、linux文本处理工具:
文本过滤工具:grep、egrep、fgrep
行编辑工具:sed
格式化文本工具:awk、gawk
注意:centos7中,awk是指向gwak的链接文件。
[root@localhost ~]# ll -l `which awk`
lrwxrwxrwx. 1 root root 4 Aug 29 20:01 /usr/bin/awk -> gawk
[root@localhost ~]#
2、awk工作原理:
awk从文本中一次读取一行,按指定的分隔符切割成若干个片段部分,默认的分隔符是“空白”字符,每个分片都保存在awk内建的变量中($1,$2,$3...),然后用动作(常用的print、printf命令)去输出这些行中的某一个分片、某几个或者整行分片。整行内容用"$0"表示,切割出来的每一分片,也可以进行加工处理。
3、awk命令:
格式:gawk [OPTIONS] 'program' FILE ... //注意program要用单引号引起来
3.1、awk的OPTIONS: -F:指定输入时的字段分割符,用于文件中读取时以什么为分割符;默认为空格分割符 -v var=value :自定义变量 | 注意:awk是文本格式化工具,但也是一种完整的编程语言,因此他向其他编程语言一样,也包含:变量(3.3节)、操作符(3.4节)、控制语句(3.5节)、数组(3.6节)、函数(3.7节),这些都可以在'program'中使用 |
3.2、awk的program的格式:
PATTERN{ACTION STATEMENT} //此处的PATTERN作用主要是界定需要编辑的行,action执行的动作,statement要执行的语句,可以有多个,语句之间以分号(;)分隔。
示例:
[root@localhost ~]# tail -3 /etc/fstab | awk '{print $2,$4}' //输出时默认以空白字符分隔片段;cut只能理解单个或固定的空白字符。
PATTERN:
①、空模式:empty,不定义pattern,表示处理文件中的每一行,即匹配每一行,
②、/regular expression/:仅处理能够被此处模式匹配到的行,两个斜线(/)必须要写
示例:[root@localhost ~]# awk '/^UUID/{print}' /etc/fstab //输出以UUID开头的行
[root@localhost ~]# awk '!/^UUID/{print}' /etc/fstab //输出不以UUID开头的行
③、relation expression:关系表达式(也叫比较表达式),结果有真有假,结果为真的才会被处理,结果为假的行不会被处理;真:结果非0值,假:结果为0值
示例:[root@localhost ~]# awk -F: '$3>=1000{print $1,$3}' /etc/passwd //打印每行UID大于1000的第一和第三个分片字段。
[root@localhost ~]# awk -F: '$NF=="/bin/bash"{print $1,$7}' /etc/passwd //匹配最后一个分片是/bin/bash的行,显示第一和第七个分片字段
[root@localhost ~]# awk -F: '$NF~"bash"{print $1,$7}' /etc/passwd //通配最后一个分片是/bin/bash的行,显示第一和第七个分片字段
④、line ranges:行范围,界定从满足第一个模式到下一个模式之间的行
格式:/pat1/,/pat2/
示例: [root@localhost ~]# awk -F: '/^root/,/^m/{print $0}' /etc/passwd //界定以root开头到以m结尾的用户,然后输出匹配项的整行内容
[root@localhost ~]# awk -F: '(NR>=2 && NR<=10){print $0}' /etc/passwd //显示行号从2到10这些行的整行内容
⑤、BEGIN{ } //仅在开始处理文件中的文本之前执行一次,
END{ } //仅在文本处理之后,命令结束之前执行一次
示例:[root@localhost ~]# head -10 /etc/passwd | awk -F: 'BEGIN{print "username uid\n============"}{print $1,$3}END{print "===end"}'
常用的action:
①、expression:运算表达式
②、control statement:控制语句,如if、while语句等
③、compound statement:组合语句
④、input statement:输入语句
⑤、outprint statement:输出语句,如print,printf
⑤.1、输出语句--print:
语法格式:print item1,item2,...
解释:多个字段(item)以逗号分隔,否则输出时是合并在一起输出的;
输出的各item字段可以是字符、数值、当前记录的字段变量或awk的表达式;
如果省略item,则输出整行内容,相当于打印"$0"
print示例:
[root@localhost ~]# tail -3 /etc/fstab | awk '{print "hello:" $1,$2}'
hello:UUID=43446f03-00c0-4d49-a4f3-2050cecfff9e /boot
hello:/dev/mapper/cl-swap swap
[root@localhost ~]# tail -3 /etc/fstab | awk '{print "hello: $1,$2"}' //print后放在双引号中的内容不会被替换成其他数值。
hello:/dev/mapper/cl-root /
hello: $1,$2
hello: $1,$2
hello: $1,$2
[root@localhost ~]#
⑤.2、输出语句--printf:格式化输出
语法格式:printf FORMAT,item1,item2,... //format为格式符,它会为每个item占一个位置,item的数值会套用format中定义的格式。
语法要点:
FORMAT必须给出
printf不会自动换行,如要换行,需要显式给出换行控制符"\n"
FORMAT中需要分别为后面的每个item指定一个格式化符号
FORMAT格式符介绍:
%c:显式字符的ASCII码
%d或%i:显式为十进制的整数
%e或%E:科学计数法数值显式
%f:显式为浮点数
%g或%G:以科学计数法或浮点形式显示数值
%s:显示为字符串
%u:显示为无符号整数
%%:显示%自身
printf示例:
[root@localhost ~]# awk -F: '{printf "username:%s\n",$1}' /etc/passwd //打印出passwd中第一个片段并套用%s格式。
[root@localhost ~]# awk -F: '{printf "username:%s,uid:%d\n",$1,$3}' /etc/passwd //除了格式符,其他放在双括号中的字符原样输出。
FORMAT格式符的修饰符:用来控制格式符的显示机制,如对齐;放在格式符前,如:%[修饰符]s
修饰符:
#[.#] :[.#]可以省略,不是通配符的意思,第一个数字"#"表示控制显示的宽度,第二个数字"#"表示小数点的精度,如果没有小数,可以省略。
示例:%3.1f
[root@localhost ~]# awk -F: '{printf "username:%10s,uid:%10d\n",$1,$3}' /etc/passwd //默认右对齐
-:左对齐,默认为右对齐
示例:[root@localhost ~]# awk -F: '{printf "username:%-10s,uid:%10d\n",$1,$3}' /etc/passwd
+:显示数值的符号,正数为“+”,负数为“-”
示例:[root@localhost ~]# awk -F: '{printf "username:%-10s,uid:%+10d\n",$1,$3}' /etc/passwd
3.3、awk的变量介绍:
内建变量: FS:input filed Seperate,输入片段分隔符,默认为空白字符 OFS:output filed seperate,输出片段分隔符,默认为空白字符 示例:[root@localhost ~]# awk -v FS=':' '{print $1,$2}' /etc/passwd //指定输入分隔符为: ,然后打印出每行的第一个和第二个分片部分 [root@localhost ~]# awk -F: '{print $1}' /etc/passwd //直接用—F选项指定分隔符: [root@localhost ~]# awk -v FS=':' -v OFS=':' '{print $1,$2}' /etc/passwd //指定输入分隔符和输出分隔符,每个-v选项定义一个变量 RS:input record Seperate:指定输入时的换行符,很少用到 ORS:outpunt reccord seperate:指定输出时的换行符,很少用到 示例:[root@localhost ~]# head -1 /etc/passwd | awk -v RS=':' '{print $0}' //指定输入时的换行符为: [root@localhost ~]# head -1 /etc/passwd | awk -v RS=':' -v ORS='#' '{print $0}' //指定输入时的换行符为:,输出换行符# NF:number of filed,统计每一行分片后的分片个数 示例:[root@localhost ~]# head -1 /etc/passwd | awk -v FS=':' '{print NF}' //统计以':'分隔后分片字段个数,结果为7,注意此处的NF不要加$符号 [root@localhost ~]# head -1 /etc/passwd | awk -v FS=':' '{print $NF}' //如果NF前加上$符号,表示引用每行的最后一个分片字段,相当于$7; 注意:awk中引用变量时不要加$符号,但是引用分片字段时要加$符号。 NR:number of reord:统计行数(会显示每一行的行号,如果有多个文件会统一编号) 示例:[root@localhost ~]# awk '{print NR,$1}' /etc/fstab //统计文件有多少行 [root@localhost ~]# awk '{print NR,$1}' /etc/fstab /etc/issue //有多个文件时会统一编号 FNR:number of reord:统计行数(会显示每一行的行号,如果有多个文件不会统一编号) 示例:[root@localhost ~]# awk '{print FNR,$1}' /etc/fstab /etc/issue //有多个文件时不会统一编号 FILENAME:显示被处理的文件名称,即被awk处理的文件的名称 示例:[root@localhost ~]# awk '{print FILENAME}' /etc/issue //会每行显示一个文件名 ARGC:bash命令行参数的个数 ARGV:数组,保存的是bash命令行所给定的各参数 示例:[root@localhost ~]# awk 'BEGIN{print ARGC}' /etc/issue //ARGC统计命令行给的参数个数 2 [root@localhost ~]# awk 'BEGIN{print ARGV[0]}' /etc/issue //引用bash命令行保存的数组参数 awk [root@localhost ~]# awk 'BEGIN{print ARGV[1]}' /etc/issue /etc/issue [root@localhost ~]# awk 'BEGIN{print ARGV[2]}' /etc/issue |
自定义变量: 方法一:-v var=value //var为变量名,value是变量值;需要注意的是:awk中的变量名区分大小写 示例: [root@localhost ~]# awk -v test='hello magedu' '{print test}' /etc/issue //print后此处引用变量不要加$符号 hello magedu hello magedu hello magedu 方法二:在program中直接定义: 示例: [root@localhost ~]# awk '{test="hello magedu";print test}' /etc/issue //多个语句间用分号分隔。 hello magedu hello magedu hello magedu [root@localhost ~]# |
3.4、awk操作符介绍:
①、算术运算操作符:
x+y、x-y、x*y、x/y、x^y、x%y、-x、+x
解释 -x:把正数变成负数;
+x:把一个字符串转成数值
②、字符串操作:没有符号的操作符表示字符串连接
③、赋值操作符:=、+=、-+、*=、/=、%=、^=、++、--
④、比较运算符:>、>=、<、<=、!=、== //注意一个=是变量赋值,两个==是做比较运算
⑤、模式匹配符:
~ :是否匹配;左侧的字符串是否能被右侧的模式所匹配
!~:是否不匹配,左侧的字符串是否不能被右侧的字符串所匹配
⑥、逻辑操作符:
&&:与
||:或
!:非
⑦、函数调用操作:
function_name(argu1,argu2,...) //和bash的函数调用不同,这里要带小括号,括号中可以有也可以没有参数
⑧、条件表达式:
selector?if-true-expression:if-false-expressiom //selector是一个条件表达式,结果又真和假之分,如果为真则执行if-true-expressiom,否则,执行假的语句。注意:真假语句之间用冒号(:)分隔。
示例:判断一个用户是系统用户还是普通用户(centos7)
[root@localhost ~]# awk -F: '{$3>=1000?usertype="commuser":usertype="systemuser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd /
3.5、awk控制语句介绍:
①、if(condition) {statement} //单分支if语句,如果statement只是一条语句,可以省略“{ }” ②、if(condition) {statements} else {statement} //双分支if语句,语句部分必须带“{ }” if语句使用场景:对awk取得的整行或某个分片字段做出条件判断 示例:[root@localhost ~]# awk -F: '{if($3>=2000){print $1,$3}}' /etc/passwd //显示出uid大于2000的用户和id [root@localhost ~]# awk -F: '{if($3>=1000){print "comm user:" $1,"uid:"$3} else {print "sysuser:" $1,"uid:"$3}}' /etc/passwd //判断用户类型 [root@localhost ~]# awk -F: '(NR>=2 && NR<=10){if($NF!="/bin/bash") {print $1,$7}}' /etc/passwd //显示第二行到第十行,默认shell不是bash的用户 [root@localhost ~]# awk '{if(NF>=5) {print $0}}' /etc/fstab //显示分片后字段数大于5的行
[root@localhost ~]# df -h //显示文件系统的空间使用情况 [root@localhost ~]# df -h | awk -F% '{print $1}' [root@localhost ~]# df -h | awk -F% '{print $1}' | awk '{if($NF>=50) {print $1,$NF}}' //显示空间使用大于50%的那些文件系统 |
③、while(condition) {statement} //当条件为真时才执行语句;注意:没必要使用while语句去遍历文件中的每一行,因为awk内置的功能就会完成遍历操作 while语句使用场景: 对一行内的多个字段逐一进行类似处理的操作,使用while语句 对数组中的各元素逐一处理操作时,使用while语句
示例:显示文本中每个满足条件的行,统计行内字段的个数和每个字段中的字符个数。 [root@localhost ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg [root@localhost ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){if(length($i)>=7) {print $i,length($i)};i++}}' /etc/grub2.cfg |
④、do {statement} while(condition) //无论条件为真或假,都先执行一次循环体 |
⑤、for(exp1;exp2;exp3) {statement} //exp1:变量初始化;exp2:条件判断;exp3:变量修正 示例:显示文本中每个满足条件的行,统计行内字段的个数和每个字段中的字符个数。 [root@localhost ~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg for循环特殊用法:能够遍历数组中的元素 语法格式:for(var in array) {for-body} //var是变量,in是关键字,array是数组名;意思是让var遍历array的索引(下标)。 |
⑥、switch语句:多分支的if语句,类似bash脚本中的case语句。 语法格式:swith(expression) {case value1:statement1;case value2:statement2;....;default:statement} 注意:expression是表达式,case是关键字,value可以是一个“值”也可以是“模式”,用来和expression做比较,如果哪个value的值与expression的值相匹配,执行其后的语句,都不匹配则执行default后的语句 |
⑦、break [n] //跳出n层循环语句 continue //提前结束本轮循环,进入下一轮循环,在awk中主要用来控制行内循环
next //用来控制awk的内生循环;作用是提前结束本行处理,进入下一行文本处理
next示例:显示用户id为偶数的用户 [root@localhost ~]# awk -F: '{if($3%2!=0){next};print $1,$3}' /etc/passwd |
⑧、delete array[index] //删除指定数组元素 delete array //删除整个数组 |
⑨、exit //退出语句 |
⑩、{statement} |
3.6、awk中的数组(arrary介绍):
awk支持的数组类型:
常规数组:下标为数字的数组
关联数组:下标为字符的数组,在awk中,此种数组用的比较多
数组定义:array[index-expression]
index-pression的表示方式:
①、可以使用任意字符串,字符串使用双引号(" ")引起,单引号用来标识program
②、如果某数组元素实现不存在,在引用时,awk会自动创建此元素索引,并将其值初始化为“空字符串”,因此要判断数组中是否存在某元素,需要使用 for(var in array) {for-body}格式进行。
示例:
[root@localhost ~]# awk 'BEGIN{weekdays["mon"]="monday";weekdays["tue"]="tuesday";print weekdays["tue"]}'
[root@localhost ~]# awk 'BEGIN{weekdays["mon"]="monday";weekdays["tue"]="tuesday";for(i in weekdays) {print weekdays[i]}}' //遍历数组中的元素,var会遍历array中的每个索引
[root@localhost ~]# netstat -tan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp6 0 0 :::111 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
用数组统计上述netstat -tan命令中状态出现的次数:
[root@localhost ~]# netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state){print i,state[i]}}'
LISTEN 5
统计/var/log/httpd/access_log-20180114日志文件中,访问的ip出现的次数
[root@localhost httpd]# awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
统计/etc/fstab文件中每个文件系统类型出现的次数
[root@localhost httpd]# awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab
xfs 1
[root@localhost httpd]#
统计指定文件中每个单词出现的次数
[root@localhost httpd]# awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab
3.7、awk中的函数:
内置函数
自定义函数
内置函数:
①、rand():返回0和1之间的一个随机数(小数)
示例:
[root@localhost httpd]# awk 'BEGIN{print rand()}' //取出的随机数如果不做处理,那么会一直保持此值不变
0.237788
[root@localhost httpd]#
②、字符串处理函数
length([s]) :返回指定字符串[s]的长度,不过不写[s]返回“0值”
sub(r,s,[t]):以r表示的模式来查找t所表示的字符串中的匹配的内容,并将其第一次出现替换为s所表示的内容;t可以省略,替换后返回的是“1”或“0”
示例:[root@localhost httpd]# awk 'BEGIN{print sub(wo,ni,nilaizhaowohaobu)}'
[root@localhost httpd]# awk '{print sub(o,O,$1)}' /etc/passwd
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符串中的匹配的内容,并将其全部出现替换为s所表示的内容;t可以省略,替换后返回的是“1”或“0”
split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中,a是一个数组,从表从“1”开始,1,2,3....
示例:[root@localhost httpd]# netstat -tan | awk '/tcp\>/{split($5,ip,":");count[ip[i]]++}END{for(i in count){print i,count[i]}}'