awk.md
简介
awk是一个强大的文本分析工具,相对于grep的查找,sed的行编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
命令格式和选项
格式
# awk [options] 'script' file1 file2, ...
# awk [options] 'PATTERN { action }' file1 file2, ...
命令选项
- -F fs or --field-separator fs:指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。
- -v var=value or --asign var=value:赋值一个用户定义变量。
- -f scripfile or --file scriptfile:从脚本文件中读取awk命令。
print输出
使用格式
print item1, item2, ...
要点
1.各项目之间使用逗号隔开,而输出时则以空白字符分隔;
2.输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;
3.print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print "";
举例
# awk 'BEGIN{print "one line.\ntwo line."}'
one line.
two line.
# awk -F : '{print $1"-->"$7}' /etc/passwd |head -1
root-->/bin/bash
# awk -F : '{print $1,$7}' /etc/passwd |head -1
root /bin/bash
awk内置变量
记录变量
- FS:读取文件本时,所使用字段分隔符,默认是任何空格;
- RS:输入文本信息所使用的换行符,默认是一个换行符;
- OFS:输出字段分隔符(默认值是一个空格)。
- ORS:输出记录分隔符(默认值是一个换行符)。
数据变量
- NR:awk命令所处理的记录数;如果有多个文件,这个数目会把处理的多个文件中行统一计数;
- NF:当前记录的field个数;
- FNR:与NR不同的是,FNR用于记录正处理的行是当前这一文件中被总共处理的行数;
- ARGV:数组,保存命令行本身这个字符串,如awk '{print $0}' a.txt b.txt这个命令中,ARGV[0]保存awk,ARGV[1]保存a.txt;
- ARGC:awk命令的参数的个数;
- FILENAME:awk命令所处理的文件的名称;
- ENVIRON:当前shell环境变量及其值的关联数组;
举例
# awk -F : '{print $1,$NF}' /etc/passwd |head -1
root /bin/bash
# awk 'BEGIN {FS=":"} {print $1,$NF}' /etc/passwd |head -1
root /bin/bash
# awk 'BEGIN {FS=":";OFS="-->"} {print $1,$NF}' /etc/passwd |head -1
root-->/bin/bash
# awk 'BEGIN {RS=" "} {print $0}' /etc/passwd
# awk 'BEGIN {ORS="@@@"} {print $0}' /etc/passwd
# awk '{print NR,$0}' /etc/issue /etc/redhat-release
1 \S
2 Kernel \r on an \m
3
4 CentOS Linux release 7.2.1511 (Core)
# awk '{print FNR,$0}' /etc/issue /etc/redhat-release
1 \S
2 Kernel \r on an \m
3
1 CentOS Linux release 7.2.1511 (Core)
自定义变量
gawk允许用户自定义自己的变量以便在程序代码中使用,变量名命名规则与大多数编程语言相同,只能使用字母、数字和下划线,且不能以数字开头。gawk变量名称区分字符大小写。
脚本中
在gawk中给变量赋值使用赋值语句进行,例如:
# awk 'BEGIN{var="variable testing";print var}'
命令行
gawk命令也可以在“脚本”外为变量赋值,并在脚本中进行引用。例如,上述的例子还可以改写为:
# awk -v var="variable testing" 'BEGIN{print var}'
printf
printf命令的使用格式:
printf format, item1, item2, ...
要点
1.其与print命令的最大不同是,printf需要指定format;
2.format用于指定后面的每个item的输出格式;
3.printf语句不会自动打印换行符;\n
format格式的指示符都以%开头,后跟一个字符;如下:
- %c: 显示字符的ASCII码;
- %d, %i:十进制整数;
- %e, %E:科学计数法显示数值;
- %f: 显示浮点数;
- %g, %G: 以科学计数法的格式或浮点数的格式显示数值;
- %s: 显示字符串;
- %u: 无符号整数;
- %%: 显示%自身;
修饰符
- N: 显示宽度;
- -: 左对齐;
- +:显示数值符号;
举例
# awk -F : '{printf "%-20s %s\n",$1,$NF}' /etc/passwd |head -1
root /bin/bash
输出重定向
格式
print items > output-file
print items >> output-file
print items | command
特殊文件描述符
/dev/stdin:标准输入
/dev/sdtout: 标准输出
/dev/stderr: 错误输出
/dev/fd/N: 某特定文件描述符,如/dev/stdin就相当于/dev/fd/0;
操作符
算术
- -x: 负值
- +x: 转换为数值;
- x^y:
- x**y: 次方
- x*y: 乘法
- x/y:除法
- x+y:
- x-y:
- x%y:
字符串操作符
只有一个,而且不用写出来,用于实现字符串连接;
赋值操作符
- =
- +=
- -=
- *=
- /=
- %=
- ^=
- **=
- ++
- --
需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代;
布尔值
awk中,任何非0值或非空字符串都为真,反之就为假;
比较操作符
- x < y True if x is less than y.
- x <= y True if x is less than or equal to y.
- x > y True if x is greater than y.
- x >= y True if x is greater than or equal to y.
- x == y True if x is equal to y.
- x != y True if x is not equal to y.
- x ~ y True if the string x matches the regexp denoted by y.
- x !~ y True if the string x does not match the regexp denoted by y.
- subscript in array True if the array array has an element with the subscript subscript.
表达式间的逻辑关系符
- &&
- ||
条件表达式
- selector?if-true-exp:if-false-exp
函数调用
- function_name(para1,para2)
常见的模式类型
1.Regexp: 正则表达式,格式为/regular expression/
2.expresssion: 表达式,其值非0或为非空字符时满足条件,如:$1 ~ /foo/ 或 $1 == "magedu",用运算符(匹配)和!(不匹配)。
3.Ranges: 指定的匹配范围,格式为pat1,pat2
4.BEGIN/END:特殊模式,仅在awk命令执行前运行一次或结束前运行一次
5.Empty(空模式):匹配任意输入行;
/正则表达式/:使用通配符的扩展集。
关系表达式:可以用下面运算符表中的关系运算符进行操作,可以是字符串或数字的比较,如$2>%1选择第二个字段比第一个字段长的行。
模式匹配表达式:
模式,模式:指定一个行的范围。该语法不能包括BEGIN和END模式。
BEGIN:让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。
END:让用户在最后一条输入记录被读取之后发生的动作。
举例
# awk -F : '$3==1000{print $0}' /etc/passwd
fei:x:1000:1000::/home/fei:/bin/bash
# awk -F : '$1~/^f/{print $0}' /etc/passwd
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
fei:x:1000:1000::/home/fei:/bin/bash
常见的Action
1.Expressions
2.Control statements:if, while等;
3.Compound statements:组合语句;
4.Input statements
5.Output statements
控制语句
if-else
语法
if (condition) {then-body} else {[ else-body ]}
举例
# awk -F: '{if ($3==0) {print $1, "Adminitrator";} else { print $1,"Common User"}}' /etc/passwd
# awk -F: '{if ($1 ~ /root/) {print $1,"is Administrator"}else {print $1,"is common"}}' /etc/passwd
# awk -F: '{if ($1 == "root") {printf "%18s %s\n",$1,"Admin"}else {printf "%18s %s\n",$1,"common"}}' /etc/passwd
# awk -F: -v sum=0 '{if ($3<1000) sum++}END{print sum}' /etc/passwd
while
语法
while (condition){statement1; statment2; ...}
举例
# awk -F: '{i=1;while (i<=3) {print $i;i++}}' /etc/passwd
# awk -F: '{i=1;while (i<=NF) { if (length($i)>=4) {print $i}; i++ }}' /etc/passwd
do-while
语法
do {statement1, statement2, ...} while (condition)
举例
# awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd
# awk -F: '{i=4;do {print $i;i--}while(i>4)}' /etc/passwd
for
语法
for ( variable assignment; condition; iteration process) { statement1, statement2, ...}
举例
# awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd
# awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd
case
语法
switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}
break 和 continue
常用于循环或case语句中
next
提前结束对本行文本的处理,并接着处理下一行;例如,下面的命令将显示其ID号为奇数的用户:
# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
awk中使用数组
语法
array[index-expression]
index-expression可以使用任意字符串;需要注意的是,如果某数据组元素事先不存在,那么在引用其时,awk会自动创建此元素并初始化为空串;因此,要判断某数据组中是否存在某元素,需要使用index in array的方式。
要遍历数组中的每一个元素,需要使用如下的特殊结构:
for (var in array) { statement1, ... }
其中,var用于引用数组下标,而不是元素值;
举例
# awk 'BEGIN{a["mon"]="Monday";a["sun"]="Sunday";print a["mon"]}'
Monday
# awk 'BEGIN{a["mon"]="Monday";a["sun"]="Sunday";print a["sun"]}'
Sunday
统计系统的用户默认shell出现次数:
# awk -F: '{BASH[$NF]++}END{for (i in BASH){printf "%15s %d\n",i,BASH[i]}}' /etc/passwd
/bin/sync 1
/bin/bash 2
/sbin/nologin 18
/sbin/halt 1
/sbin/shutdown 1
统计系统的网络连接状态的次数:
netstat -tan|awk '/^tcp\>/{state[$NF]++}END{for (a in state){printf "%15s %d\n",a,state[a]}}'
统计httpd日志中访问量前十的IP地址的访问次数:
awk '{ip[$1]++}END{for (i in ip){printf "%18s %d\n",i,ip[i]}}' access_20170119.log |sort -k2 -rn|head
统计日志中各状态码的访问次数:
awk '{ip[$9]++}END{for (i in ip){printf "%5s %d\n",i,ip[i]}}' access_20170119.log |sort -k2 -rn|head
统计每个用户的进程的占了多少内存(注:sum的RSS那一列):
ps aux |awk 'NR!=1{user[$1]+=$6}END{for (i in user){print i,user[i]}}'
polkitd 11804
dbus 1868
postfix 7804
root 114984
内置函数
spilt
用法
split(string, array [, fieldsep [, seps ] ])
功能:将string表示的字符串以fieldsep为分隔符进行分隔,并将分隔后的结果保存至array为名的数组中;数组下标为从1开始的序列;
举例
显示连接服务器中IP最多的10个:
netstat -tan|awk '/:80\>/{split($5,clients,":");IP[clients[4]]++}END{for (i in IP){printf "%20s %d\n",i,IP[i]}}' |sort -k2 -rn |head
显示系统中磁盘空间使用超过20%的分区:
df -lh | awk '!/^File/{split($5,percent,"%");if(percent[1]>=20){print $1}}'
统计当前系统上每个客户端IP的连接中处于TIME_WAIT的连接状态的个数:
netstat -tan |awk '/TIME_WAIT/{split($5,clients,":");IP[clients[4]]++}END{for (i in IP){printf "%20s %d\n",i,IP[i]}}' |sort -k2 -rn |head
统计ps aux命令执行时,当前系统上各状态的进程的个数:
ps aux |awk '!/^USER/{state[$8]++}END{for (i in state){printf "%10s %d\n",i,state[i]}}' |sort -k2 -rn
统计ps aux命令执行时,当前系统上各用户的进程的个数:
ps aux |awk '!/^USER/{state[$1]++}END{for (i in state){printf "%10s %d\n",i,state[i]}}' |sort -k2 -rn
显示ps aux命令执行时,当前系统上其VSZ(虚拟内存集)大于10000的进程及其PID:
ps aux|awk '!/^USER/{if ($5>10000) print $2,$5,$NF}'
length
length([string])
功能:返回string字符串中字符的个数;
substr
substr(string, start [, length])
功能:取string字符串中的子串,从start开始,取length个;start从1开始计数;
system
system(command)
功能:执行系统command并将结果返回至awk命令
扩展学习
http://www.cnblogs.com/repository/archive/2011/05/13/2045927.html
http://www.runoob.com/linux/linux-comm-awk.html
http://www.cnblogs.com/ggjucheng/archive/2013/01/13/2858470.html
http://coolshell.cn/articles/9070.html