AWK是一个优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称得自于它的创始人阿尔佛雷德·艾侯、彼得·温伯格和布莱恩·柯林汉姓氏的首个字母)的最大功能取决于一个人所拥有的知识。awk经过改进生成的新的版本nawk,gawk,现在默认linux系统下日常使用的是gawk,用命令可以查看正在应用的awk的来源(ls -l /bin/awk)

    awk是一款强大的报告日志生成处理工具,不同于sed和grep,它的侧重点是如何把文本信息更好的展示出来,常用与统计和格式化输出。awk相当于微型的shell,有着自己一套语法结构,例如:循环结构,数组,条件判断,函数,内置变量等功能。处理对象一般纯文本文件或纯文本信息,同时它也能与正则表达式完全兼容使用。


awk应用详解:

awk基本语法结构:

awk [options] '/pattern/{action}' file1 file2 ...
#awk [可选参数] '模式{动作}'    文件1 文件2 ...

awk [options] 'script' file1 file2 ...
#awk [可选参数] '脚本'  文件1 文件2 ...

---------------------------------------------------------------------------------

awk结果输出

print     item1,item2,...
#打印显示 项目1,项目2,...

结果输出显示应用详解

(1)各项目之间使用逗号分隔,而输出时则使用输出分隔符分隔。

(2)输出的各item(项目)可以是字符串、数值、当前记录的字段、变量或awk的表达式,数值会被隐式转换为字符串后输出。

(3)print后面item如果省略,相当于print $0,输出所有数据;若输出空白,则使用print ""。

示例如下:

使用print格式输出显示当前系统下所有用户名和用户使用的shell

# awk -F: '{print $1,$7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync

使用print格式输出显示当前系统下所有用户的所有信息

# awk -F: '{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

使用print格式输出显示当前系统下所有用户的所有信息为空输出

# awk -F: '{print ""}' /etc/passwd

awk模式应用:

(1)Regexp: 格式为/pattern/,仅处理被/pattern/匹配到的行
(2)Expression: 表达式,其结果为非0或非空字符串时满足条件,仅处理满足条件的行
(3)Ranges: 行范围,此前地址定界,startline, endline,仅处理范围内的行
(4) BEGIN/END: 特殊模式,仅在awk命令的program运行之前(BEGIN)或运行之后(END)执行一次
(5)Empty:空模式,匹配任意行


awk的变量应用
内置变量

FS:Field Seperator, 输入时的字段分隔符;
RS:Record Seperator, 输出行分隔符;
OFS: Output Field Seperator, 输出时的字段分隔符;
ORS: Outpput Row Seperator, 输出时的行分隔符;

示例如下:

使用指定输入字段分隔符为":"显示/etc/passwd用户名和用户登录使用的shell。

# awk 'BEGIN{FS=":"}/root/{print $1,$7}' /etc/passwd
root /bin/bash
operator /sbin/nologin

使用指定输入字段分隔符为":",输出字段分隔符为"#",显示/etc/passwd用户名和用户登录使用的shell。

# awk 'BEGIN{FS=":";OFS="#"}/root/{print $1,$7}' /etc/passwd
root#/bin/bash
operator#/sbin/nologin

使用指定输入字段分隔符为":",输出字段分隔符为"#",输出时的行分隔符,显示/etc/passwd用户名和用户登录使用的shell。

# awk 'BEGIN{FS=":";OFS="#";ORS="*"}/root/{print $1,$7}' /etc/passwd
root#/bin/bash*operator#/sbin/nologin*

--------------------------------------------------------------------------------------------------------

NR:Numbers of Record,行数;所有文件一并计数

显示所处理文件的行数(合并计数):

[root@iptablesRouter ~]# awk '{print NR,$0}' /etc/issue /etc/fstab 
1 CentOS release 6.5 (Final)
2 Kernel \r on an \m
3 
4 
5 #
6 # /etc/fstab
7 # Created by anaconda on Fri Mar 18 16:36:32 2016
8 #
9 # Accessible filesystems, by reference, are maintained under '/dev/disk'
10 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
11 #
12 /dev/mapper/vg0-root    /                       ext4    defaults        1 1
13 UUID=26e4890e-309c-4afb-842e-218614b31f49 /boot                   ext4    defaults        1 2
14 /dev/mapper/vg0-usr     /usr                    ext4    defaults        1 2
15 /dev/mapper/vg0-var     /var                    ext4    defaults        1 2
16 /dev/mapper/vg0-swap    swap                    swap    defaults        0 0
17 tmpfs                   /dev/shm                tmpfs   defaults        0 0
18 devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
19 sysfs                   /sys                    sysfs   defaults        0 0
20 proc                    /proc                   proc    defaults        0 0

--------------------------------------------------------------------------------------------------------

NF:Numbers of Field,字段数

显示所有处理文件的每行的字段数:

# awk '{print NF,$0}' /etc/issue /etc/fstab 
4 CentOS release 6.5 (Final)
5 Kernel \r on an \m
0 
0 
1 #
2 # /etc/fstab
10 # Created by anaconda on Fri Mar 18 16:36:32 2016
1 #
9 # Accessible filesystems, by reference, are maintained under '/dev/disk'
12 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
1 #
6 /dev/mapper/vg0-root    /                       ext4    defaults        1 1
6 UUID=26e4890e-309c-4afb-842e-218614b31f49 /boot                   ext4    defaults        1 2
6 /dev/mapper/vg0-usr     /usr                    ext4    defaults        1 2
6 /dev/mapper/vg0-var     /var                    ext4    defaults        1 2
6 /dev/mapper/vg0-swap    swap                    swap    defaults        0 0
6 tmpfs                   /dev/shm                tmpfs   defaults        0 0
6 devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
6 sysfs                   /sys                    sysfs   defaults        0 0
6 proc                    /proc                   proc    defaults        0 0

--------------------------------------------------------------------------------------------------------

FNR:行数;多文件处理时分别对文件计数。

显示所处理文件的行数(分别计数):

# awk '{print FNR,$0}' /etc/issue /etc/fstab 
1 CentOS release 6.5 (Final)
2 Kernel \r on an \m
3 
1 
2 #
3 # /etc/fstab
4 # Created by anaconda on Fri Mar 18 16:36:32 2016
5 #
6 # Accessible filesystems, by reference, are maintained under '/dev/disk'
7 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
8 #
9 /dev/mapper/vg0-root    /                       ext4    defaults        1 1
10 UUID=26e4890e-309c-4afb-842e-218614b31f49 /boot                   ext4    defaults        1 2
11 /dev/mapper/vg0-usr     /usr                    ext4    defaults        1 2
12 /dev/mapper/vg0-var     /var                    ext4    defaults        1 2
13 /dev/mapper/vg0-swap    swap                    swap    defaults        0 0
14 tmpfs                   /dev/shm                tmpfs   defaults        0 0
15 devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
16 sysfs                   /sys                    sysfs   defaults        0 0
17 proc                    /proc                   proc    defaults        0 0

--------------------------------------------------------------------------------------------------------

ARGV: 数组,保存命令行本身这个字符串,如awk '{print $0}' a.txt b.txt这个命令中,ARGV[0]保存awk,ARGV[1]保存a.txt;ARGV[2]保存b.txt
ARGC: awk命令的参数的个数;
FILENAME: awk命令所处理的文件的名称;
ENVIRON:当前shell环境变量及其值的关联数组;

数组示例如下:

显示当前awk数组参数保存的信息

# awk '{print ARGV[0], ARGV[1],ARGV[2]}' open.sh samlee.sh 
awk open.sh samlee.sh

显示当前awk参数的个数

# awk '{print ARGC}' open.sh samlee.sh 
3

显示当前awk正在处理的文件名称

# awk '{print FILENAME}' open.sh samlee.sh 
open.sh
samlee.sh


用户自定义变量

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

自定义变量声明:

-v var_name=VALUE    #变量名区分字符大小写


(1)在脚本中赋值变量
在gawk中给变量赋值使用赋值语句进行,

例如:
awk 'BEGIN{var="variable testing";print var}'

(2)gawk命令也可以在“脚本”外为变量赋值,并在脚本中进行引用。

例如,上述的例子还可以改写为:
awk -v var="variable testing" 'BEGIN{print var}'

示例如下:

# awk 'BEGIN{S="samlee";print S}'
samlee
# awk -v S="samlee" 'BEGIN{print S}'
samlee


awk-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: 显示宽度;
-: 左对齐;
+:显示数值符号;

示例如下:

使用printf输出显示自定义变量值的ASCII码

# awk 'BEGIN{str=70;printf "%c\n",str}'
F

使用printf输出显示自定义变量值保留2位小数

# awk 'BEGIN{str=4.131254;printf "%.2f\n",str}'
4.13

使用printf输出显示/etc/passwd用户名和UID按列指定位置显示

# awk -F: '{printf "%+10s %-5d\n",$1,$3}' /etc/passwd
      root 0    
       bin 1    
    daemon 2    
       adm 3    
        lp 4    
      sync 5    
  shutdown 6    
      halt 7    
      mail 8    
      uucp 10   
  operator 11   
     games 12   
    gopher 13 
# awk -F: '{printf "%+10s %i\n",$1,$3}' /etc/passwd
      root 0    
       bin 1    
    daemon 2    
       adm 3    
        lp 4    
      sync 5    
  shutdown 6    
      halt 7    
      mail 8    
      uucp 10   
  operator 11   
     games 12   
    gopher 13

awk输出重定向应用

print items > output-file     #保存到某文件
print items >> output-file    #追加到某文件
print items | command         #使用管道交给某些命令处理
 
特殊文件描述符:
    /dev/stdin: 标准输入
    /dev/stdout: 标准输出
    /dev/stderr: 错误输出
    /dev/fd/N: 某特定文件描述符,如/dev/stdin就相当于/dev/fd/0;

示例如下:

将错误信息输出

# awk -F: '{printf "%-15s %i\n",$1,$3 > "/dev/stderr" }' /etc/passwd

将信息输出指定文件中保存

# awk -F" " '{printf "%-10s %d\n",$1,$3 > "/tmp/outinfo"}' /etc/passwd
# cat /tmp/outinfo 
root:x:0:0:root:/root:/bin/bash 0
bin:x:1:1:bin:/bin:/sbin/nologin 0


awk操作符应用

算术操作符:
-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    模式匹配,左边的字符串能够被右边的模式所匹配为真,否则为假
x !~ y    不匹配为真,匹配为假
subscript in array      True if the array array has an element with the subscript subscript.

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

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

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

a=3
b=4
a>b?a is max:b ia max


函数调用:
function_name(para1,para2)

示例如下:

判断UID是否大于等于500,如果为真就显示为普通用户,如果为假就显示为系统或管理用户。

# awk -F: '{$3>=500?utype="common user":utype="admin or system user";print $1,"is" utype}' /etc/passwd
root isadmin or system user
bin isadmin or system user
daemon isadmin or system user
adm isadmin or system user
lp isadmin or system user


常用的action
Expressions:表达式 赋值等
Control statements:条件语句,控制语句if while…
Compound statements:组合语句
input statements:输入语句
output statements:输出语句


条件控制语句:

if-else格式:

语法:if (condition) {then-body} else {[ else-body ]}

示例如下:

输出显示uid>=500的用户显示为普通用户,否则显示为系统管理用户。

# awk -F: '{if ($3>=500) {print $1,"is a common user"} else {print $1,"is an admin or system user"}}' /etc/passwd
root is an admin or system user
bin is an admin or system user
daemon is an admin or system user
adm is an admin or system user
lp is an admin or system user

输出显示uid=0的用户显示为系统管理员用户,否则显示为普通用户。

# awk '{if ($3==0) {print $1, "Adminitrator";} else { print $1,"Common User"}}' /etc/passwd

输出显示用户为root标识为系统管理员,否则显示普通管理用户。

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

输出显示uid>=500的用户数量

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

显示文件中字段数大于等于8的行

# awk '{if (NF>=8) {print}}' /etc/inittab

输出显示uid>=500的用户

# awk -F : '{if ($3>=500) {print $0}}' /etc/passwd
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
888:x:500:500::/home/888:/bin/bash
hadoop:x:501:501::/home/hadoop:/bin/bash


while循环控制

while格式如下:

语法: while (condition){statement1; statment2; ...}

示例如下:

显示所有用户的用户名、口令、uid字段

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

只输出每行大于4的字段

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

只显示文件的奇数行

# awk '{i=1; while (i<=NF){printf "%s ",$i;i+=2};print " "}' /etc/inittab

do-while循环

 do-while 至少执行一次循环体,不管条件满足与否

语法: do {statement1, statement2, ...} while (condition)

显示所有用户的用户名、口令、uid字段

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

输出1到100之和

# awk 'BEGIN{sum=0;i=0;do{sum+=i;i++;}while(i<=100) print sum;}'
5050

for循环控制

语法: for ( variable assignment; condition; iteration process) { statement1, statement2, ...}

显示所有用户的用户名、口令、uid字段

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

只输出每行大于4的字段

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


for循环还可以用来遍历数组元素

语法: for (i in array) {statement1, statement2, ...}

汇总各个shell使用的用户数量

awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /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 -F: '{if(NR%2==0) next;print NR,$1,$3}' /etc/passwd


 

awk中使用数组
 数组声明格式:

array[index-expression]

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

要遍历数组中的每一个元素,需要使用如下的特殊结构:

for (var in array) { statement1, ... }   #其中,var用于引用数组下标,而不是元素值;

示例如下:

统计每一种网络连接状态出现的次数

# netstat -tan|awk '/^tcp/{++state[$NF]}END{for (s in state) {print s,state[s]}}'
ESTABLISHED 5
LISTEN 20
每出现一被/^tcp/模式匹配到的行,数组state[$NF]就加1,NF为当前匹配到的行的最后一个字段,此处用其值做为数组state的元素索引;

统计web服务访问日志中的ip访问量

# awk '{counts[$1]++}; END {for(url in counts) print url,counts[url]}' /var/log/httpd/access_log
172.16.0.1 209
#用法与上一个例子相同,用于统计某日志文件中IP地的访问量


删除数组变量
从关系数组中删除数组索引需要使用delete命令。

使用格式为:

delete  array[index]

awk的内置函数

split(string,array[,fieldsep[,seps]])
功能:将string表示的字符串以fieldsep为分隔符进行切片,并切片后的结果保存至array为名的数组中;数组下标从1开始
此函数有返回值,返回值为切片后的元素的个数

统计连接请求的IP地址

# netstat -tn|awk '/^tcp/{lens=split($5,client,":");ip[client[lens-1]]++}END{for(i in ip) print i,ip[i]}'
172.16.0.1 2
127.0.0.1 2
205.177.69.146 2
length([string])
功能:返回string字符串中字符的个数;

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

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

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

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

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

综合案例:

统计ps aux命令执行时,当前系统上各状态的进程的个数;

# ps aux|awk '!/^USER/{state[$8]++}END{for (i in state) {printf "%-15s %d\n",i,state[i]}}'

统计ps aux命令执行时,当前系统上各用户的进程的个数;

# ps aux |awk '!/^USER/{state[$1]++}END{for (i in state) {printf "%s %d\n",i,state[i]}}'

显示ps aux命令执行时,当前系统上其VSZ(虚拟内存集)大于10000的进程及其PID;

# ps aux|awk '!/^USER/{if ($5<10000) next;print $2,$5,$NF}'
# ps aux|awk '!/^USER/{if ($5>10000) print $2,$5,$NF}'

以上Linux文本处理必杀技之awk应用详解所有内容。