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

posted @ 2017-03-13 00:53  ProfiBus  阅读(226)  评论(0编辑  收藏  举报