文本处理三剑客:
grep、egrep、fgrep:称为文本过滤工具
sed:称为流编辑器,行编辑器
awk:称为报告生成器,主要用于格式化文本输出
awk:Aho,Weinberher,Kernighan
由这个三个作者创建而成,最初是用在UXIN系统上面,后来被GNU组织不断对其进行更新,就形成了gawk,现在linux上用的都是gawk。
[root@localhost tmp]# which awk /bin/awk [root@localhost tmp]# ll -d /bin/awk lrwxrwxrwx. 1 root root 4 Mar 11 17:28 /bin/awk -> gawk //awk就是gawk的链接文件
awk的基本用法:
gawk [options] 'program' FILE ...
program: PAT{aciton STATEMENS}
STATEMENS(语句): 多个语句之间用分号分隔
1、常用选项:
-F:指明输入输出时用到的分隔符,默认是空白字符
-v var=varlue:自定义变量
awk中的变量:
1):内建变量
FS:表示输入时的分隔符;默认是空白字符
[root@localhost tmp]# awk -v FS=' ' '{print $1}' /etc/passwd //以空白作为分隔符 root:x:0:0:root:/root:/bin/bash ftp:x:14:50:FTP //后面还有User:/var/ftp:/sbin/nologin
OFS:表示输出时的分隔符;默认为空白字符
[root@localhost tmp]# awk -v FS=':' -v OFS=':' '{print $1}' /etc/passwd root bin
RS:表示输入的行分隔符,意思就是换行符
[root@localhost tmp]# awk -v RS=':' '{print}' /etc/passwd root x 0 0 root /root /bin/bash
ORS:表示输出的行分隔符,
[root@localhost tmp]# awk -v RS=':' -v ORS='#' '{print}' /etc/passwd root#x#0#0#root#/root#/bin/bash
NF:表示每行的字段数量
[root@localhost tmp]# awk -F: '{print NF}' /etc/fstab 0 1 1
如果在{ }里面加上个$,$NF表示的是每行的最后一个字段,所以注意在括号里面引用内建变量或已定义的变量不需要加$
[root@localhost tmp]# awk -F: '{print $NF}' /etc/passwd /bin/bash /sbin/nologin
NR:打印行数,表示总记录数
FNR:打印行数,表示当前的记录数
[root@localhost tmp]# awk '{print NR}' file1 file2 1 2 //NR=FNR表示只处理12345行的内容,相当于只处理file1的内容 3 4 5 6 7 [root@localhost tmp]# awk '{print FNR}' file1 file2 1 2 //NR>FNR表示处理567行的内容,相当于只处理file2的内容 3 4 1 2 3
示例:有两个文件file1、file2,内容分别是
1:beijing:1322676::: 1:x:d3dn1
2:shanghai:222676::: 2:y:DWHh0
3:Guangdong:342676::: 3:z:hWX9Y
求 awk 'BEGIN{OFS=FS=":"}NR==FNR{a[$1]=$2;}NR>FNR{$2=a[$1];print}' file1 file2 的结果
[root@localhost tmp]# awk 'BEGIN{OFS=FS=":"}NR==FNR{a[$1]=$2;}NR>FNR{$2=a[$1];print}' file1 file2 1:beijing:d3dn1 2:shanghai:DWHh0 3:Guangdong:hWX9Y
NR==FNR{a[$1]=$2;} :把file1中的$2赋值给a[$1]数组中,分别是1=beijing 2=shanghai 3=Guangdong
NR>FNR{$2=a[$1] :把数组的元素赋值给file2中$2,然后输出
FILENAME:输出当前文件名
文件有几行就输出多少次名字
[root@localhost tmp]# awk '{print FILENAME}' /etc/issue /etc/issue /etc/issue /etc/issue
ARGC:命令行参数的个数
[root@localhost tmp]# awk 'BEGIN{print ARGC}' /etc/issue 2
ARGV:数组,保存的是命令行所给的的各参数
[root@localhost tmp]# awk 'BEGIN{print ARGV[0]}' /etc/issue awk [root@localhost tmp]# awk 'BEGIN{print ARGV[1]}' /etc/issue /etc/issue
自定义变量:
1、 -v var=value //变量名区分大小写
[root@localhost tmp]# awk -v test="hello" 'BEGIN{print test}' hello
2、也可以在{ }中直接给出
[root@localhost tmp]# awk 'BEGIN{test="hello";print test}' hello //注意print前面要有;
常用的action:
1、print em1,em2.....
要点:
1.逗号分隔符;
2.输出的各item可以字符串,也可以是数值,当前记录的字段、变量或awk的表达式
{print "hello",$1,$2}
{print "hello",$1,$2,6}
3.如省略item,相当于print $0;
2、printf
格式化输出:printf FORMAT ,item1,item2,...
格式符
1)、FORMAT是必须要给出
2)、不会自动换行,需要显示则给出换行符,\n
3)、FORMAT中需要分布为后面的每个ietm指定一个格式化符号;
多个FORMAT用可以自己用(:,空格)随便指定,显示时会根据指定的分隔符显示
支持的格式符有:
%c:显示字符的ASCII码
%d,%i: 显示十进制整数
$e,$E: 科学计数法数值显示
%f : 显示为浮点数
%g,%G:
%s: 显示字符串
%u: 无符号整数
%%: 显示%本身
[root@localhost tmp]# awk -F: '{printf "Username: %s UUID: %d\n",$1,$3}' /etc/passwd Username: root UUID: 0 Username: bin UUID: 1
支持的修饰符有:
#[.#]: 第一个数字控制显示的宽度:第二个#表示小数点后的精度
表示方式:%3s %3.1s
- :左对齐,默认右对齐
[root@localhost tmp]# awk -F: '{printf "Username: %-8s UUID: %-2d\n",$1,$3}' /etc/passwd Username: root UUID: 0 Username: bin UUID: 1
awk也支持操作符:
算术操作符:
x+y,x-y,x*y,x/y,x^y,x%y
-x
+x:转换为数值;
字符串操作符:没有符号的操作符表示字符串连接
赋值操作符:
=,+=,-=,*=,/=,%=,^=
++,--
比较操作符:
>,>=,<,<=,!=,== 模式匹配符:
~:是否匹配右侧字符串
!~ 是否不匹配右侧字符串
逻辑操作符:
&& :
|| :
! :
函数调用:
function_name(argu1,argu2,...)
awk还支持内建的条件表达式:
selector? if-true-expression: if-false-expression
条件表达式 如果为真执行这个 为假执行这个
[root@localhost tmp]# awk -F: '{$3>1000?usertype="Common User":usertype="System User";printf "%-5s:%-1s\n",usertype,$1}' /etc/passwd System User:root System User:bin
awk支持匹配模式:
1、empty:空模式,匹配每一行
2、/regular expression/:仅处理能够被此处的模式匹配到的行
[root@localhost tmp]# awk '/^UUID/{print}' /etc/fstab UUID=98a6a73c-6b57-479f-947f-2d201a17c950 / ext4 defaults 1 1 UUID=361beea7-5d62-4c33-b317-c93fbc4e0650 /boot ext4 defaults 1 2 UUID=d2750a4a-9a19-45c7-a54e-3bb22db4c1b3 swap swap defaults 0 0
3、relational expression:关系表达式:结果有“真”有“假”;结果为真才会被处理 真:结果为非0值,非空字符串 假:结果为0值
[root@localhost tmp]# awk -F: '$3>1000{print $1,$3}' /etc/passwd user11 2000 mageedu 3001 mageed 3002 auser2 3003 msql 3004 gentoo 3005 centos 3006
[root@localhost tmp]# awk -F: '$NF~"/bash$"{print $1,$7}' /etc/passwd root /bin/bash user1111 /bin/bash user222 /bin/bash
4、line ranges:行范围,地址定界
startline,endline 不支持直接给出数字的格式
/pat1/,/pat2/
[root@localhost tmp]# awk -F: '/^root/,/^daemon/{print $1}' /etc/passwd root bin daemon
5、BEGIN/END模式
BEGIN{}:在开始处理文件中文本之前仅执行一次,无论文本是否存在
[root@localhost tmp]# awk 'BEGIN{print "Username\n--------------"}' Username -------------- [root@localhost tmp]# awk -F: '{print "Username\n--------------\n",$1}' /etc/passwd Username //不加BEGIN每行都会输出一次 -------------- root Username -------------- bin
END{}: 在文本处理完成之后执行一次
[root@localhost tmp]# awk -F: '{print "Username\n--------------\n"}{print $1}END{print "#######"}' /etc/passwd Username -------------- named #######
awk还支持控制语句:
1、‘if语句
{if(condition) {statements}’ //使用在对awk去的整行或某个字段做条件判断
‘{if(condition) {statments} else {statements}’
[root@localhost tmp]# awk -F: '{if($3>1000) {print $1,$3}}' /etc/passwd user11 2000 mageedu 3001 [root@localhost tmp]# awk -F: '{if($3>1000) {print "User:" $1}else {print "SystemUser:" $1}}' /etc/passwd SystemUser:root SystemUser:bin
2、while循环 // 对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用。
语法: while(condition) {statement}
条件“真”,进入循环:条件“假”,退出循环
比如要计算某个文件里每个字段的的字符长度,那就需要用到while循环
[root@localhost tmp]# awk -F' ' '/^[[:space:]]*kernel/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)};i++}}' /etc/grub.conf /vmlinuz-2.6.34.14 18 root=UUID=98a6a73c-6b57-479f-947f-2d201a17c950 46
i:表示字段,length($i):表示返回字符长度,i++:表示显示下一个字段
3、for循环
语法: ‘{for(expr1;expr2;expr3) statement}’
[root@localhost tmp]# awk -F' ' '/^[[:space:]]*kernel/{for(i=1;i<=NF;i++) {print $i,length($i)};i++}' /etc/grub.conf kernel 6 /vmlinuz-2.6.34.14 18 ro 2 root=UUID=98a6a73c-6b57-479f-947f-2d201a17c950 46
特殊用法:
能够遍历数组中的元素:
语法:for(var in array) {for-body}
[root@localhost tmp]# awk 'BEGIN{weekday["mon"]="Monday";weekday["tue"]="Tuesday";for(i in weekday) {print weekday[i]}}' Monday Tuesday
4、break和continue
5、next 提前结束对本行的处理而进入下一行
[root@localhost tmp]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd root 0 daemon 2 //取出偶数行
awk还支持数组
支持关联数组: array[index-expression]
数组元素下标是从1开始
index-expression:
1、可使用任意字符串,字符串要用" "括起来
2、如果某数组元素事先不存在,在引用时,awk会自动
创建此元素,并将其值初始化为“空“
示例:统计/etc/fstab文件中每个文件系统出现的次数
[root@localhost tmp]# awk '/^UUID/{count[$3]++}END{for (i in count){print i,count[i]}}' /etc/fstab swap 1 ext4 2
数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空“
count[$3]++:初始值为空,每读到一个相同元素就加1
i:表示数字索引 count[i]:表示数组元素
练习:统计指定文件中每个单词出现的次数
[root@localhost tmp]# awk '{for (i=1;i<=NF;i++){count[$i]++}}END{for (i in count){print i,count[i]}}' /etc/fstab mount(8) 1 Accessible 1 pages 1 reference, 1
awk还支持函数:
内置函数
数值处理:
rand():返回0和1之间一个随机数
[root@localhost tmp]# awk 'BEGIN{print rand()}' 0.237788
字符串处理:
length([s]):返回指定字符串的长度
sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容
并将其第一次出现出现替换为s所表示的字符串。
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容
并将其替换为s所表示的字符串。
split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数字中;
示例:求出访问本机中的服务器的总数
[root@localhost tmp]# netstat -tan |awk '/^tcp/{split($5,ip,":");print ip[1]}' |awk -v RS='' '{print}'|awk '{count[$1]++}END{for (i in count) {print i,count[i]}}' 0.0.0.0 2 172.18.252.65 1