文本处理工具-AWK
awk简介
awk功能与sed相似,都是用来进行文本处理的。awk可以自动地搜索输入文件,并把每一个输入行切分成字段。许多工作都是自动完成的,例如读取每个输入行、字段分割。
awk工作原理
awk一次从文本内容中读取一行文本,按输入分隔符进行切,也可以使用-F选项指定分隔符,切成多个组成部分,将每段内容直接保存在内建的变量中$1,$2,$3....$NF(最后一列),引用指定的变量,可以显示指定断,或者多个断。如果需要显示全部的,需要使用$0来引用。可以对单个片断进行判断,也可以对所有断进行循环判断。其默认分隔符为空格
基础应用示例
选项:
-F 指明输入时用到的字段分隔符
-v var=value: 自定义变量
awk最常用的格式为:awk [options] 'pattern{action}' file
最常用的action是:print
一个示例:
[root@yufu ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 4.8G 1.6G 3.0G 35% /
/dev/sda1 190M 37M 144M 21% /boot
/dev/sda3 2.8G 1.1G 1.6G 39% /home
处理:awk可以处理多列
[root@yufu ~]# df -h | awk '{print $1}'
Filesystem
/dev/sda2
/dev/sda1
/dev/sda3
[root@yufu ~]# df -h | awk '{print $1,$5}'
Filesystem Use%
/dev/sda2 35%
/dev/sda1 21%
/dev/sda3 39%
$0与$NF,NF,$NF-1
$0整行输出
[root@yufu ~]# df -h | awk '{print $0}'
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 4.8G 1.6G 3.0G 35% /
/dev/sda1 190M 37M 144M 21% /boot
/dev/sda3 2.8G 1.1G 1.6G 39% /home
$NF的尾列输出
[root@yufu ~]# df -h | awk '{print $NF}'
on
/
/boot
/home
NF是统计每一行的总列数
[root@yufu ~]# df -h | awk '{print NF}'
7
6
6
6
$(NF-1)倒数第二列
[root@yufu ~]# df -h | awk '{print $1,$(NF-1)}'
Filesystem Mounted
/dev/sda2 35%
/dev/sda1 21%
/dev/sda3 39%
自定义插入字符
在输出文本列中,还可以添加自己要插入的内容
默认输出的字段之间间隙比较小,可以在字段键插入tab键分开距离
[root@yufu ~]# df -h | awk '{print $1"\t"$2}'
Filesystem Size
/dev/sda2 4.8G
/dev/sda1 190M
/dev/sda3 2.8G
也可以是输出内容更具可读性
[root@yufu ~]# df -h | awk '{print $1" 磁盘大小:"$2}'
Filesystem 磁盘大小:Size
/dev/sda2 磁盘大小:4.8G
/dev/sda1 磁盘大小:190M
/dev/sda3 磁盘大小:2.8G
awk两种特殊模式
BEGIN和END是awk的两种特殊模式
BEGIN模式指定了处理文件前要执行的操作
END模式是指处理完所有行后要执行的操作
下面用一个示例来看:在磁盘使用率上加上一行标题,最后一行加上end标识
[root@yufu ~]# df -h | awk 'BEGIN{print "this is disk usage status:"} {print $1"\t"$5}'
this is disk usage status:
Filesystem Use%
/dev/sda2 35%
/dev/sda1 21%
/dev/sda3 39%
[root@yufu ~]# df -h | awk 'BEGIN{print "this is disk usage status:"} {print $1"\t"$5} END{print "end"}'
this is disk usage status:
Filesystem Use%
/dev/sda2 35%
/dev/sda1 21%
/dev/sda3 39%
end
awk分隔符
awk的分隔符分两种:“输入分隔符”和“输出分隔符”:
输入分隔符 : 简称 FS;默认的输入分隔符是空格,awk默认以空格为分隔符将文件进行列的分割。
输出分隔符 简称OFS;awk将文本切割处理后输出在屏幕上,以什么字符作为字段的分隔符?默认也是为空白字符最为输出分隔符。
在处理文本时,如果文本中没有空白字符那怎么进行分割?这时需要使用输入分隔符指定其他的字符作为分隔符进行切片处理,其实这里的 FS和上面示例中使用的-F作用是一样的,都是指定输入的分割符,同时,FS定义的分隔符可以在Action中进行引用作为输出时插入的内容
使用FS指定分隔符
[root@yufu ~]# awk -v FS=':' '{print $1,$3}' /etc/passwd | head -n 3
root 0
bin 1
daemon 2
[root@yufu ~]# awk -v FS=':' '{print $1FS$3}' /etc/passwd | head -n 3
root:0
bin:1
daemon:2
awk处理输出的内容默认是以空白字符作为列的分隔符,如果要指定输出内容的分割符可以使用OFS来指定,一个列子对比
[root@yufu ~]# df -h | awk '{print $1,$5}'
Filesystem Use%
/dev/sda2 35%
/dev/sda1 21%
/dev/sda3 39%
[root@yufu ~]# df -h | awk -v OFS=------ '{print $1,$5}'
Filesystem------Use%
/dev/sda2------35%
/dev/sda1------21%
/dev/sda3------39%
awk变量
内置变量
在上面的示例中已经使用到了变量,awk变量分为“内置变量”和“自定义变量”,示例中用到的NF、FS、OFS等就是awk的自定义变量,内置变量是awk预置好的,而自定义变量需要用户自己定义。
awk常用的内置变量如下:
FS:输入字段分隔符,默认为空白字符
OFS:输出字段分隔符,默认为空白字符
RS:输入记录的分隔符(换行符),指定输入时的换行符
NF:当前行的字段数(被分割成的列数),$NF 为最后一列,两者不同
NR:行号,当前处理文本的行号(也可以使用NR指定输出哪一行)
FNR:各文件分别计数的行号
FILENAME:当前文件名
ARGC:命令行参数的个数
ARGV:数组,保存的是命令行所给定的各个参数
上面已经写了FS和OFS的使用,接着看看RS,RS直白理解就是定义文本内容中以什么作为行的分隔符,有时候会用到这种方式定义行的分割
一个列子:fstab文件以 双引号 作为行的分隔符
[root@yufu opt]# cat /etc/fstab
UUID="6ec772b7-9efb-46d0-80a5-4deaf6d6efe0" /boot ext4 defaults 0 0
UUID="96cb6b8f-c3da-41d1-a063-cfd0e8177085" / ext4 defaults 0 0
UUID="6c323417-2b44-48fd-a4b3-074085b5fe95" /home ext4 defaults 0 0
UUID="fc02879d-56bb-4153-8fa9-c21aa9af8b19" swap swap defaults 0 0
[root@yufu opt]# cat /etc/fstab | awk -v RS='"' '{print $1}'
UUID=
6ec772b7-9efb-46d0-80a5-4deaf6d6efe0
/boot
96cb6b8f-c3da-41d1-a063-cfd0e8177085
/
6c323417-2b44-48fd-a4b3-074085b5fe95
/home
fc02879d-56bb-4153-8fa9-c21aa9af8b19
swap
ORS指定输出分隔符
OFS可以指定输出时字段间用什么样的字符作为分隔符。默认为空格,这个功能与OFS作用差不多,个人感觉没有OFS好用。两个例子对比
ORS输出分割(乱遭遭的,看不懂)
[root@yufu opt]# df -h | awk -v ORS='----' '{print $1,$2}'
Filesystem Size----/dev/sda2 4.8G----/dev/sda1 190M----/dev/sda3 2.8G
OFS输出分割
[root@yufu opt]# df -h | awk -v OFS=---- '{print $1,$2}'
Filesystem----Size
/dev/sda2----4.8G
/dev/sda1----190M
/dev/sda3----2.8G
NF和$NF的用法
稍不注意会被这两个给搞混了,NF是统计一行中的列数,而$NF是取一行中的最后一列
一个例子对比
[root@yufu opt]# cat /etc/fstab
UUID="6ec772b7-9efb-46d0-80a5-4deaf6d6efe0" /boot ext4 defaults 0 0
UUID="96cb6b8f-c3da-41d1-a063-cfd0e8177085" / ext4 defaults 0 0
UUID="6c323417-2b44-48fd-a4b3-074085b5fe95" /home ext4 defaults 0 0
UUID="fc02879d-56bb-4153-8fa9-c21aa9af8b19" swap swap defaults 0 0
[root@yufu opt]# awk '{print NF,$NF}' /etc/fstab
6 0
6 0
6 0
6 0
每行以空格分割,一个6列。最后一列是0
NR行号处理
NR可以用来显示处理行的行号,也可以用来匹配指定行的内容
示例
[root@yufu opt]# df -h | awk '{print NR,$0}'
1 Filesystem Size Used Avail Use% Mounted on
2 /dev/sda2 4.8G 1.6G 3.0G 36% /
3 /dev/sda1 190M 37M 144M 21% /boot
4 /dev/sda3 2.8G 1.1G 1.6G 39% /home
指定行号处理
[root@yufu opt]# df -h | awk 'NR==2 {print $1,$5}'
/dev/sda2 36%
指定行号输出
[root@yufu opt]# df -h | awk 'NR==2'
/dev/sda2 4.8G 1.6G 3.0G 36% /
FNR:用于分别显示两个文件行号
[root@yufu opt]# awk '{print FNR,$0}' fi.txt fr.txt
1 hfjg
2 jsklyhfg
3 kifg
1 jkdfgufgjk
2 sdfg
ARGV内置变量表示的是一个数组,这个数组中保存的是命令行所给定的参数,数组的下标从0开始
一个列子
[root@yufu opt]# awk 'BEGIN{print "aaa",ARGV[1]}' fi.txt fr.txt
aaa fi.txt
[root@yufu opt]# awk 'BEGIN{print "aaa",ARGV[0]}' fi.txt fr.txt
aaa awk
[root@yufu opt]# awk 'BEGIN{print "aaa",ARGV[2]}' fi.txt fr.txt
aaa fr.txt
自定义变量
自定义变量容易理解,就是我们自己定义的变量,定义的方法:
自定义变量: -v varname=value (变量名区分大小写)
[root@yufu opt]# awk -v myvar="gudaoyufu" 'BEGIN{print myvar}'
gudaoyufu
或者
[root@yufu opt]# awk 'BEGIN{myvar="gudaoyufu";print myvar}'
gudaoyufu
awk格式化输出
在输出时可以是内容一一些特定的格式输出,格式化输出则需要使用printf来指定,功能与print相似,只是这个可以支持格式化输出
格式化输出:printf “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
printf示例
[root@yufu opt]# awk -F : '{printf "%-15s %-10d\n",$1,$3}' /etc/passwd | head -n 4
root 0
bin 1
daemon 2
adm 3
[root@yufu opt]# awk -F : '{printf "-15s UID:%-10d\n",$1,$31}' /etc/passwd | head -n 4
root UID:0
bin UID:1
daemon UID:2
adm UID:3
awk运算操作
上面有个示例:
[root@yufu opt]# awk 'NR==2 {print $1}' /etc/fstab
UUID="96cb6b8f-c3da-41d1-a063-cfd0e8177085"
这个就使用到了运算符,“NR==2”,获取一个行并做处理,awk支持多种运算处理
算术操作符:
x+y, x-y, x*y, x/y, x^y, x%y
-x: 转换为负数
+x: 转换为数值
字符串操作符:没有符号的操作符,字符串连接
赋值操作符:
=, +=, -=, *=, /=, %=, ^=
++, --
比较操作符:
==, !=, >, >=, <, <=
逻辑操作符:与&&,或||,非!
简单示例
[root@yufu opt]# awk -F : '$3>=600 {print $1,$3}' /etc/passwd
suzhou 600
yufu 601
guoguo 602
user2 604
user3 605
user4 606
user5 607
user6 608
user7 609
user8 610
user9 611
user10 612
[root@yufu opt]# awk -F : '$3 <100 || $3 >=600 {print $1,$3}' /etc/passwd
root 0
bin 1
dbus 81
vcsa 69
postfix 89
sshd 74
oprofile 16
... ...
suzhou 600
yufu 601
guoguo 602
匹配磁盘使用情况
[root@yufu opt]# df -h|awk -F% '/^\/dev/{print $1}'|awk '$NF>=30{print $1,$5}'
/dev/sda2 36
/dev/sda3 39
awk的PATTERN匹配
PATTERN:根据pattern条件,过滤匹配的行,再做处理,PATTERN中可以使用正则表达式来进行文本匹配
,例如,匹配passwd中以user开头的用户
[root@yufu opt]# awk '/^user/{print $0}' /etc/passwd
user2:x:604:604::/home/user2:/bin/bash
user3:x:605:605::/home/user3:/bin/bash
user4:x:606:606::/home/user4:/bin/bash
user5:x:607:607::/home/user5:/bin/bash
user6:x:608:608::/home/user6:/bin/bash
user7:x:609:609::/home/user7:/bin/bash
user8:x:610:610::/home/user8:/bin/bash
user9:x:611:611::/home/user9:/bin/bash
user10:x:612:612::/home/user10:/bin/bash
上面这个功能实现了grep的匹配功能,但它的使用与grep还是有些区别的:
grep "正则表达式" /etc/passwd
awk '/正则表达式/ {print $0}' /etc/passwd
在awk使用正则表达式时特殊字符要使用转义符;如下:
[root@yufu opt]# awk '/\/bin\/bash$/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
suzhou:x:600:600::/home/feng:/bin/bash
tomcat:x:501:501::/home/tomcat:/bin/bash
yufu:x:601:601::/home/yufu:/bin/bash
guoguo:x:602:602::/home/guoguo:/bin/bash
user2:x:604:604::/home/user2:/bin/bash
... ...
结构控制语句
在awk中我们同样可以if这样的语法进行条件判断,只是在awk中使用if判断条件是在一行中完成,下面的一个示例:查看使用率超过30的分区
[root@yufu ~]# df -h | awk '/^\/dev\/sd/{if ($5>=30){print $1,$5}}'
/dev/sda2 36%
/dev/sda3 39%
如果判断条件不成立则进行下面的动作:
[root@yufu ~]# df -h | awk '/^\/dev\/sd/{if ($5>=50){print $1,$5}else{print $1,$5}}'
/dev/sda2 36%
/dev/sda1 21%
/dev/sda3 39%
awk数组使用
数组的格式:
name[0]="xiaoming"
name[1]="laowang"
上列中name为数组名,[0],[1]为元素下标 ,“小明”为数组元素
定义并打印一个简单的数组
[root@yufu ~]# awk 'BEGIN {a[0]="gudao";a[1]="yufu";print a[0],a[1]}'
gudao yufu
打印数的下标
[root@yufu ~]# awk 'BEGIN {a[0]="gudao";a[1]="yufu";for (i in a){print i}}'
0
1
把数组的下标和元素一起打印
[root@yufu ~]# awk 'BEGIN {a[0]="gudao";a[1]="yufu";for (i in a){print i, a[1]}}'
0 yufu
1 yufu
数组遍历
数组遍历需要使用for循环来配合处理,下面以一个示例看看awk的数组遍历的使用:
示例:一个web日志文件有60多万条的记录,怎样统计日志里每个ip的访问次数呢?
[root@yufu ~]# head access_log
192.168.30.6 - - [14/May/2018:14:21:41 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
方法一:
[root@yufu ~]# cat access_log | awk '{print $1}' | sort |uniq -c | sort -nr
295764 172.20.109.251
170000 172.20.109.233
100000 172.20.101.240
39243 172.20.111.90
16000 172.20.109.164
8000 172.20.102.79
6062 172.20.111.9
6000 172.20.103.69
4031 172.20.101.23
4014 192.168.30.6
4000 172.20.1.24
2056 172.20.111.65
2020 172.20.111.164
2002 172.20.110.34
2000 172.20.111.162
400 172.20.75.66
58 ::1
10 192.168.30.1
方法二:用AWK数组遍历来统计
[root@yufu ~]# awk '{num[$1]++}END{for(i in num){print i,num[i]}}' access_log | sort -nr -k2
172.20.109.251 295764
172.20.109.233 170000
172.20.101.240 100000
172.20.111.90 39243
172.20.109.164 16000
172.20.102.79 8000
172.20.111.9 6062
172.20.103.69 6000
172.20.101.23 4031
192.168.30.6 4014
172.20.1.24 4000
172.20.111.65 2056
172.20.111.164 2020
172.20.110.34 2002
172.20.111.162 2000
172.20.75.66 400
::1 58
192.168.30.1 10
理解上面的例子:
awk中使用的num[$1]++ :数组中将日志文件的第一列作为数组num的下标,awk在读取第一行时会读取到这个数组,此时数组是这样的:num[172.20.109.251]++,由于后边的++运算符,awk会将数字0自动赋值给num[172.20.109.251]然后再做++运算。
此时num[172.20.109.251]做++,也就是0+1=1
当在读到第二个172.20.109.251时,此时num[172.20.109.251]的值已经为1,再做一次—++运算就是1+1得到的值为2,就这样以此类推
最后的++运算的值是多少,也就意味着172.20.109.251这个ip被运算了多少次,也就意味着这个地址出现了多少次。
最后通过for循环把num数组的下标(ip)和出现的次数都打印出来
同样,可以利用awk数组统计tcp的连接数:
[root@centos ~]# netstat -ant | awk '/^tcp/{state[$NF]++}END{for(i in state){print i,"\t"state[i]}}'
TIME_WAIT 19
CLOSE_WAIT 1
ESTABLISHED 5
LISTEN 11
还可以统计当前系的80端口的连接数与远程连接的ip
[root@web ~]# netstat -nat | grep "ESTABLISHED" | awk '/^tcp/{state[$(NF-2)]++}END{for(a in state){print a " " state[a]}}' | grep ':80'
192.168.19.47:80 4
文本处理工具-AWK的更多相关文章
- 文本处理工具awk
目录 gawk:模式扫描和处理语言 awk语言 awk工作原理 print awk变量 自定义变量 printf命令 awk操作符 awk的条件判断 awk的循环 awk的数组 awk的函数 awk调 ...
- linux 文本分析工具---awk命令(7/1)
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各 ...
- 文本分析工具awk简单示例
先创建一个文件:vim hi 取第2个字段和第3个字段: awk '{print $2,$3}' hi 注意{}中的,逗号会在输出的时候转变为空格 加入字符说明: 显示整行: 指定字段分隔符: ...
- AWK文本处理工具(Linux)
AWK文本处理工具(Linux) PS:刚开始实习,就给了个处理百万级别数据的任务,以前学过SHELL的一些东西sed/awk之类的处理,但是也没有具体的应用,只是在10几行10几列的小数据操作过,所 ...
- Pyp 替代sed,awk的文本处理工具
Linux上文本处理工具虽不少,像cut,tr,join,split,paste,sort,uniq,sed,awk这些经典工具让人眼花缭乱,而且都太老了,使用方法都不太人性化,尤其awk,语法简直反 ...
- 三大文本处理工具grep、sed及awk的简单介绍
grep.sed和awk都是文本处理工具,虽然都是文本处理工具单却都有各自的优缺点,一种文本处理命令是不能被另一个完全替换的,否则也不会出现三个文本处理命令了.只不过,相比较而言,sed和awk功能更 ...
- Unix文本处理工具之awk
Unix命令行下输入的命令是文本,输出也都是文本.因此,掌握Unix文本处理工具是很重要的一种能力.awk是Unix常用的文本处理工具中的一种,它是以其发明者(Aho,Weinberger和Kerni ...
- 【Linux】 字符串和文本处理工具 grep & sed & awk
Linux字符串&文本处理工具 因为用linux的时候主要用到的还是字符交互界面,所以对字符串的处理变得十分重要.这篇介绍三个常用的字符串处理工具,包括grep,sed和awk ■ grep ...
- Linux的文本处理工具浅谈-awk sed grep
Linux的文本处理工具浅谈 awk 老大 [功能说明] 用于文本处理的语言(取行,过滤),支持正则 NR代表行数,$n取某一列,$NF最后一列 NR==20,NR==30 从20行到30行 FS ...
随机推荐
- 但是你没有【But you didn't.】【by Anonymous】
作者是一位普通的美国妇女,她的丈夫在女儿4岁时应征入伍去了越南战场,从此她便和女儿相依为命.后来,她的丈夫.孩子的爸爸不幸阵亡.她终身守寡,直至年老病逝.她女儿在整理遗物时发现了母亲当年写给父亲的这首 ...
- pat04-树5. File Transfer (25)
04-树5. File Transfer (25) 时间限制 150 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue We have ...
- python 中文分词:结巴分词
中文分词是中文文本处理的一个基础性工作,结巴分词利用进行中文分词.其基本实现原理有三点: 基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG) 采用了动态规 ...
- HDU 4349——Xiao Ming's Hope——————【Lucas定理】
Xiao Ming's Hope Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- MVC切片编程
在商城网站中,用户中心的每个页面都要几乎都要涉及对用户是否登录的判断,为了减少代码重写,可采用切片编程 using System; using System.Collections.Generic; ...
- #与javascript:void(0)的区别
#"包含了一个位置信息 默认的锚点是#top 也就是网页的上端 而javascript:void(0) 仅仅表示一个死链接 这就是为什么有的时候页面很长浏览链接明明是#可是跳动到了页首 而 ...
- springboot2.x如何添加事务
什么时候需要添加事务呢?一般情况下,如果该方法有两条SQL语句或者以上都需要添加(个人感觉:)). 首先需要在我们的启动类加上 @EnableTransactionManagement //开启事务管 ...
- Redis入门--(一)简介NoSQL
1.什么是NoSql? 2.为什么需要NoSQL? 互联网经历了1.0和2.0的发展: web1.0 是早期新浪,雅虎等只能浏览,不能交互: 传统关系型数据库在应付web2.0这种动态网站的时候力不从 ...
- Python函数(2)
一.函数对象 函数是第一类对象:指的是函数名指向的值可以被当作数据去使用. 1.函数可以被引用 例如: 2.可以当作参数传递给另一个函数 例如: 3.可以当作一个函数的返回值 例如: 4.可以当作容器 ...
- ZR国庆Round2解题报告
心路历程 预计得分:100 + 10 - 20 + 10 = 120 实际得分:100 + 0 + 10 = 110 感觉这场打的挺稳的.开场秒掉A题,写+调差不多1h 然后刚T3暴力,刚完还有2h左 ...