awk简介

awk是一个强大的报告生成工具,它可以根据我们输入的信息,将其格式化以后显示在屏幕上。所谓格式化就是根据我们自定义的格式将所需要的信息比较美观的显示出来。awk最初是在1977年完成的,后来又发表了一个比之前功能更加强大的awk,叫做nwak或gawk。nawk是工作在windows操作系统下的,而gawk是GNU组织开发的。他用于linux/unix系统下的。在RHEL 5.8中,awk是gawk的软链接文件。其实awk也是一门编程语言,它也支持多种控制语句,例如:if...else...、while循环、for循环以及各种操作符等等。关于更多的awk的使用我们将在下文一一详解。


awk的工作原理

awk会读取文本中的每一行,并使用特定的分隔符将其分为N段(或者N列),然后将其显示出来(这是一种执行动作,大多数情况下为print或printf,即打印的意思)。其中每一段都可以使用一个位置变量来表示,即$1,$2,$3...。$1表示第一段,$2表示第二段,后续一次类推。其中$0表示处理的每一行的内容。


awk的语法结构

awk  [option]  'PATTERN {ACTION}'  file1  file2 ...

常用的option:

-F :指定分隔符,默认分隔符为空白

-v  var_name=#:用于在命令行中给变量赋值


在讲述awk的PATTERN和ACTION之前,我们在这里先介绍一下awk的变量以及操作符,后续介绍的PATTERN和ACTION中会使用到awk的变量以及操作符。



一、awk的变量

awk的变量分为内置变量和自定义变量

1、awk内置变量又分为记录变量和数据变量

1.1、awk内置变量之记录变量

FS: field separator,awk读取文件本时,所使用字段分隔符;

例如:

wKiom1ST5imzzWwZAABG4-cQrgk817.jpg

RS: Record separator,awk输入文本信息所使用的换行符;

OFS: Output Filed Separator: awk输出文本信息时,使用的字段分割符

例如:

wKiom1ST52bQvD0IAAB0-prSMqQ137.jpg

ORS:Output Row Separator:awk输出文本信息时,使用的换行符


1.2、awk内置变量之数据变量

NR: The number of input records,awk命令所处理行的行号;如果有多个文件,这个命令会把处理的多个文件中的行累加计数;

例如:

wKioL1SRoxXAx0pHAAArWx1s0vQ409.jpg

wKiom1XchC2y6yxIAAAuoPawTCY623.jpg

NF:Number of Field,当前行记录的field(片段或列)个数,即每一行分割的总段数;

例如:

[root@mail ~]# tail -n 1  /etc/passwd | awk -F: '{print NF}' 

7


FNR: 与NR不同的是,FNR用于记录当前文件被处理行的行号;

例如:

wKiom1SRomST89kVAAApsch0SZc262.jpg

wKiom1XchKTwPKIzAAEPrsh45RQ596.jpg

ARGV: 该变量表示一个数组,保存awk整个命令行中的所有参数,不包括PATTERN和ACTION

例如:

wKiom1SRoIji8mkwAABpjQeV6b4864.jpg

其中ARGV[0]表示第一个参数即awk,ARGV[1]表示第二个参数即a.txt,ARGV[2]表示第三个参数即b.txt    

ARGC: 该变量表示awk命令中参数的个数,这些参数不包括PATTERN和ACTION,但包括awk这个命令;

例如:

wKioL1SRoN6AEoYJAAArB2kAhgc911.jpg  

由于a.txt和b.txt文件中都只含2行数据,因此共四行


FILENAME: 表示awk命令所处理的当前文件的文件名称;

例如:

wKioL1SRoejjqDYxAAA9KN4-1WA306.jpg

由于a.txt这个文件中有2行,因此就显示2行文件


ENVIRON:这是一个当前shell环境变量及其值的关联数组;关于awk的数组在下文介绍。

例如:

wKiom1SRoB2wZFUIAACOHrHrUkw126.jpg


2、awk中自定义变量

gawk允许用户自定义自己的变量以便在程序代码中使用,变量名命名规则与大多数编程语言相同,只能使用字母、数字和下划线,且不能以数字开头。gawk变量名称区分字符大小写。

2.1、在脚本中使用自定义变量,并进行赋值

在gawk中给变量赋值使用赋值语句进行,例如:
#awk 'BEGIN{var="variable testing";print var}'

#variable testing

 

2.2、在命令行中使用自定义变量并进行赋值操作

gawk命令也可以在“脚本”外自定义变量并进行赋值,并在脚本中进行引用。例如,上述的例子还可以改写为:
#awk -v var="variable testing" 'BEGIN{print var}'

#variable testing


二、awk的输出命令

在awk中,大多数使用到的输出工具为print和printf,print和printf也是属于ACTION,且大多数要执行的ACTION都会用到print或printf。因此,掌握print和printf的使用是学习awk必不可少的工具。

1、print的使用

print的使用格式:
print item1, item2, ...
print具有如下特性:
(1)、各项目之间使用逗号隔开,而输出时则以空白字符分隔;
(2)、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;

例如:

[root@localhost ~]# awk 'BEGIN{print "nice",1}'
nice 1

(3)、print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print "";

例如:

[root@localhost ~]# tail -n 1 /etc/passwd | awk '{print }'
vuser:x:505:505::/var/ftproot:/sbin/nologin
[root@localhost ~]# tail -n 1 /etc/passwd | awk '{print ""}'

[root@localhost ~]#

 

2、printf的使用

printf可以根据自己定义的输出格式输出内容

printf命令的使用格式:
printf “format”, item1, item2, ...

printf具有如下特性:
1、其与print命令的最大不同是,printf需要指定format,按照自己定义的格式输出;
2、format用于指定后面的每个item的输出格式;
3、printf语句不会自动打印换行符;换行符为\n

 

format格式的指示符都以%开头,后跟一个字符;如下:
%c: 显示字符的ASCII码;
%d, %i:显示十进制整数;
%e, %E:以科学计数法来显示数值;
%f: 显示浮点数;
%g, %G: 以科学计数法的格式或浮点数的格式显示数值;
%s: 显示字符串;
%u: 显示无符号整数;
%%: 显示%自身;

例如:

[root@localhost ~]# head -n 3 /etc/passwd | awk -F: '{printf "%c\n",$1}'
r
b
d
[root@localhost ~]# head -n 3 /etc/passwd | awk -F: '{printf "%s\n",$1}'
root
bin
daemon
[root@localhost ~]# head -n 3 /etc/passwd | awk -F: '{printf "%e\n",$1}'
0.000000e+00
0.000000e+00
0.000000e+00

 

format后面还可以界各种修饰符,这些修饰符如下:

N: 显示宽度;
-: 左对齐;默认为右对齐
+:显示数值符号;

例如:

wKioL1SS3_SB3mtGAAA-trXlzDs061.jpg


三、输出重定向

使用print或printf时,可以将输出的内容保存在其他文件中,默认print和printf的行为是将结果打印在屏幕上。

其输出重定向的语法格式为:

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; 

 

例如:将/etc/passwd文件中的账号和uid保存在/root/c.txt文件中,该文件必须事先存在,否则会报语法错误。

wKioL1SS5WfRiCeuAACF69gnyEc106.jpg


四、awk的操作符

4.1 算术操作符:

-x: 负值
+x: 转换为数值,取值长度为遇到第一个字符则结束;

例如:

wKioL1SS6GeyLN5VAABfovutj98213.jpg

x^y:次方
x**y: 次方和上面一样的效果
x*y: 乘法
x/y:除法
x+y:加法
x-y:减法
x%y:取余

 

4.2、 字符串操作符:
只有一个,而且不用写出来,用于实现字符串连接;

 

4.3 赋值操作符:
=:
+=:
-=:
*=:
/=:
%=:
^=:
**=:

++:

--:

需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代;

 

4.4 布尔值

在awk中,任何非0值或非空字符串都为真,反之就为假;

 

4.5 比较操作符:
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这个模式,则为真;否则为假;y是一个模式
x !~ y True if the string x does not match the regexp denoted by y.

这个表示如果x不匹配y这个模式,结果则为真;否则为假;y是一个模式
subscript in array   True if the array array has an element with the subscript subscript.

 

注意:在关系表达式中,不仅仅只有数值才可以进行比较,字符串也可以进行比较,字符存比较的是字符的长度

例如:选择第二个字段比第一个字段长的行

$2  >  $1

 

4.6 表达式间的逻辑关系符:
&&
||

 

4.7 条件表达式:
selector?if-true-exp:if-false-exp

这个表示如果selector为真,则结果为if-true-exp;否则结果为if-false-exp

其实条件表达式完全可以使用if...else...语句来代替

形如:

if selector; then
  if-true-exp
else
  if-false-exp
fi


五、awk的PATTERN(模式)

PATTERN是用来匹配要处理的行的,如果PATTERN为空,则匹配所有行。

常见的模式类型:
1、Regexp: 正则表达式,格式为/regular expression/

例如:

[root@localhost ~]# awk -F:  '/^r.*/{ print $1,$3 }' /etc/passwd
root 0
rpc 32
rpcuser 29

 

2、expresssion: 表达式,其值非0或为非空字符时满足条件,如:$1 ~ /foo/ 或 $1 == "magedu",用运算符~(匹配)和!~(不匹配)。

例如:将/etc/passwd这个文件中用户的shell为bash的账号、uid和shel打印出来

[root@localhost ~]# awk -F: '$NF ~ /.*bash$/{print $1,$3,$NF}'   /etc/passwd
root 0 /bin/bash
hadoop 500 /bin/bash
xsl 501 /bin/bash
xsl2 502 /bin/bash
test 503 /bin/bash
linux 504 /bin/bash
mysql 27 /bin/bash

 

3、Ranges:格式为/pattern1/,/pattern2/;表示指定行的匹配范围,范围为pattern1第一次匹配到的行到pattern2第一次匹配到的行这之间的所有行。并且该范围不包括BEGIN和END中的内容。

 

4、BEGIN/END:特殊模式,仅在awk命令执行前运行一次或结束前运行一次

其中BEGIN后面的第一个{}中的内容表示在awk执行之前要进行的动作,通常可在这里设置全局变量。

END后面的第一个{}中的内容表示在最后一行被处理完成之后要进行的动作

例如:

wKioL1SS_eXihGGoAABrKnD44NA455.jpg

 

5、Empty(空模式):匹配任意输入行;


六、awk的ACTION(处理动作)

PATTERN是用来匹配要处理的行的,而ACTION则是对匹配的行执行相应的动作, 常见的Action有:
1、Expressions:
2、Control statements:控制语句将在后文介绍
3、Compound statements
4、Input statements
5、Output statements


 七、ACTION中的控制语句

7.1、控制语句if...else...

其语法结构为:if (condition) {then-body} [else { else-body }]

例题1:在/etc/passwd这个文件中,如果账号为"root",则显示为"Admin";否则显示为"Common User"

#awk -F: '{if ($1=="root"){print $1,"ADMIN"} else {print $1,"Common User"}}'  /etc/passwd

#awk -F: '{if ($1=="root"){printf "%-15s%15s\n",$1,"Admin"}else {printf "%-15s%15s\n",$1,"Common User"}}' /etc/passwd

例题2:显示/etc/passwd这个文件中uid>=500的用户的个数

#awk -F:  -v sum=0 '{if ($3>=500){sum++}}END{print sum}'  /etc/passwd

 

 

7.2、控制语句之while循环

语法格式:while (condition) {statement1;statement2;..}

例题1:显示/etc/passwd这个文件中每一行前三个字段

#awk -F:  '{i=1;while (i<=3)  {print $i;i++}}'  /etc/passwd

例题2:显示/etc/passwd这个文件中字段长度大于4个字符的字段

#awk  -F: '{i=1;while (i<=NF)  {if (length($i)>4) {print $i};i++}}'   /etc/passwd

 

7.3、控制语句之do...while..循环

do...while...循环与while循环稍有一点区别,do...while循环是先执行do后面{}中的内容,然后再判断while()中的条件是否成立;成立则再次执行do{}内的语句;不成立则退出循环;依次类推。

语法格式:do  {statement1;statement2;...}  while (condition)

例题:显示/etc/passwd这个文件中每一行前三个字段

#awk -F: '{i=1;do {print $i;i++} while (i<=3)}'  /etc/passwd

 

7.4、控制语句之for循环

awk中的for循环类似于c语言中的for循环格式

其语法格式为:for  ( variable assignment;condition;iteration process)  {statement1;statement2....}

例题1:显示/etc/passwd这个文件中每一行前三个字段

#awk  -F: '{for (i=1;i<=3;i++)  {print $i}}'    /etc/passwd

例题2:显示/etc/passwd这个文件中字段长度大于4个字符的字段

#awk -F:  '{ for (i=1;i<=NF;i++) {if (length($i)>4) {print $i}} }'   /etc/passwd

    

 7.5、控制语句之case语句

语法格式:switch  (expression)  { case VALUE or /REGEXP/: statement1;statement2;.. default:statement1;...}

 

7.6、break和continue

这两个关键字常用于循环和case语句中,其意义与bash中的意义是一样的。

 

7.7、关键字next

提前结束对本行文本的处理,并接着处理下一行。

例如:显示/etc/passwd这个文件中uid为奇数的用户

#awk  -F:  '{if ($3%2==0)  next;print $1,$3}'  /etc/passwd


八、awk中的数组

8.1、数组的使用

语法格式为:array_name[index]

index可以使用任意字符串;需要注意的是,如果某数组元素事先不存在,那么在引用其时,awk会自动创建此元素并初始化为空串;因此,要判断某数据组中是否存在某元素,需要使用index in array的方式。

要遍历数组中的每一个元素,需要使用如下的特殊结构:
for (index in array_name) { statement1; ... }
其中,index用于引用数组下标,而不是元素值;

例题1:利用for循环来遍历数组,来统计每个类型的shell出现的次数

# awk -F: '$NF !~ /^$/{bash[$NF]++}END{for (id in bash) {print id,bash[id]}}'  /etc/passwd

说明:这里引用的bash数组其里面的所有元素为空,然后再执行bash[$NF]++

 

例题2:统计当前系统上所有tcp协议的状态和状态次数

#netstat -ant  | awk '/^tcp/ {++S[$NF]}END{ for (zt in S) {print zt,S[zt]}}'

 

例题3:统计httpd日志文件中每个客户ip的访问量

#awk '{count[$1]++}END{for (url in count){print url,count[url]}}' /var/log/httpd/access.log

 

8.2、删除数组变量

从关系数组中删除数组索引需要使用delete命令。使用格式为:

delete  array[index]


九、awk的内置函数

split(string, array [, fieldsep [, seps ] ])
功能:将string表示的字符串以fieldsep为分隔符进行分隔,并将分隔后的结果保存至array为名的数组中;数组下标为从1开始的序列;seps表示以多少个字符为单位进行分割。

 

length([string])
功能:返回string字符串中字符的个数;

 

substr(string, start [, length])
功能:取string字符串中的子串,从start开始,取length个;start从1开始计数;

例如:

[root@localhost ~]# awk 'BEGIN{s=substr("hello nice",2,2);print s}'
el

 

system(command)
功能:执行系统command并将结果返回至awk命令

 

systime()
功能:取系统当前时间戳

例如:[root@localhost ~]# awk 'BEGIN{s=systime();print s}'
1419578753

这里的时间指的是当前时间到1970-1-1  00:00:00 所经过的秒数

tolower(s)
功能:将s中的所有字母转为小写

 

toupper(s)
功能:将s中的所有字母转为大写

例如:

[root@localhost ~]# head -n 2 /etc/passwd | awk -F: '{str=toupper($1);print  str}'
ROOT
BIN


 十、用户自定义函数

10.1、自定义函数使用function关键字。格式如下:

function F_NAME([variable])
{
 statements
}

函数还可以使用return语句返回值,格式为“return value”。

 

10.2、函数调用

function_name (para1,para2)