awk:
文本处理三剑客:grep系,sed,awk
grep系:grep,egrep,fgrep,基于PATTERN进行文本过滤;
sed:流编辑器,逐行编辑器;模式空间,保持空间;
awk:报告生成器;格式化文档输出;
awk是下面三个人的姓氏缩写:
最早由Alfred Aho开发的,曾工作于贝尔实验室;
Peter J Weinberger:贝尔实验室;
Brian W Kernighan:贝尔实验室;K&R这两个人共同编写了 《C语言程序设计 第二版》
被各种改版的awk:
CentOS/RHEL系:GNU awk,gawk;
New awk,nawk;
Old awk,oawk;
gawk命令:
gawk - pattern scanning and processing language
格式:
gawk [ options ] 'program' file ...
program:[/PATTERN/]{ACTION statement; ...}
PATTERN部分:决定动作语句何时触发以及通过什么事件来触发;
BEGIN, END
ACTION statement:对数据进行特定的处理,通常放置在{}中;
print, printf
基本概念:
分隔符:
输入分隔符:
awk对数据进行处理时,会根据特定的标识符号对数据进行分段处理,这种标识符号就称为"输入分隔符",默认的输入分隔符是空白字符;如果指定的分隔符并没有被数据包括,则只有换行分隔符有效;
输出分隔符:
awk对数据处理完成后,会以特定的标识符号对各个字段进行连接后输出,这种标识符号就称为"输出分隔符",默认的输出分隔符是空白字符;
记录:由换行符进行分隔的数据中一行,就称为一条记录(Record);通常在使用awk处理数据时,使用内置变量$0来保存整个记录的内容;
字段:经过分隔符分隔之后的每一个数据分段,都称为一个字段(Field);通常在使用awk处理数据时,使用$1,$2...$NF(Number of Field)等内置变量来存储各个字段的数据;
awk的工作原理:
1.首先,执行BEGIN{ACTION statement;...}语句块中的语句;
2.其次,从文件中或着标准输入读取一行,根据PATTERN的匹配结果执行后面的ACTION语句块中的内容;然后逐行重复该过程已完成数据处理,直到数据全部被读取完毕;
3.最后,在所有的语句块都执行完成之后,退出awk进程之前,执行END{ACTION statement;...}语句块中的语句;
注意:
1) BEGIN语句块在awk开始处理数据内容之前就被执行;通常用于生成表头;此语句块是可选语句块;
2) END语句块在处理完所有的数据之后,才会被执行;通常用于数据汇总;此语句块是可选语句块;
3) PATTERN语句块中的通用命令是最重要的部分,因此PATTERN语句块不能省略,但其中的ACTION可以省略,如果省略,则默认执行print动作,即:显示数据的各行;
4) awk在执行PATTERN语句块时,默认循环遍历数据中的各个记录;
常用选项:
-f,--file program-file:从指定的文件中加载program语句块,而不是通过命令行给出相关的程序内容;
-F,--field-separator fs:指定字段的输入分隔符;默认是空白字符;
-v,--assign var=val:用于声明自定义变量并为变量赋值;
awk的常用用法:
1.变量:
内建变量:
FS:input field separator,输入字段分隔符,默认为空白字符;
OFS:output field separator,输出字段分隔符,默认为空白字符;
示例:
~]# awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd
root:0:/bin/bash
bin:1:/sbin/nologin
daemon:2:/sbin/nologin
...
RS:The input record separator,输入记录(行)分隔符,默认为换行符;
注意:即使指定了额外的输入记录分隔符,原有的换行符依然有效;
ORS:The output record separator,输出记录分隔符,默认为换行符;
示例:
~]# awk -v RS=':' -v ORS='#' '{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
...
NF:The number of fields,每一行中的字段的总数;
示例:
~]# awk -F: '{print NF}' /etc/passwd
7
7
7
7
7
...
显示指定文件中每行中的字段的数量;
~]# awk -F: '{print $NF}' /etc/passwd
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/sync
...
显示指定文件中每行中最后一个字段的内容;
~]# awk -F: '{print $(NF-1)}' /etc/passwd
/root
/bin
/sbin
/var/adm
/var/spool/lpd
/sbin
/sbin
...
显示指定文件中每行中倒数第二个字段的内容;
NR:The total number of input records,行的总数;如果仅处理一个文件,可以将NR的值当作此文件中各行的行号;
示例:
~]# awk '{print NR}' /etc/fstab
1
2
3
4
5
...
FNR:The input record number in the current input file,对于不同的文件分别统计其行的数量;即使处理多个文件,也可以显示每个文件中每行的行号;
示例:
~]# awk '{print FNR}' /etc/fstab /etc/issue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
FILENAME:The name of the current input file,当前正在被处理的文件的文件名;
示例:
~]# awk 'END{print FILENAME}' /etc/fstab
/etc/fstab
ARGC:The number of command line arguments,命令行中参数的数量;包括awk命令本身,但不包括awk命令的选项部分和program部分;
示例:
~]# awk 'END{print ARGC}' /etc/fstab /etc/issue /etc/passwd
4
ARGV:Array of command line arguments,由命令行中所有的参数构成的数组;
示例:
~]# awk 'END{print ARGV[1]}' /etc/fstab /etc/issue /etc/passwd
/etc/fstab
显示命令行中的第N个参数;
自定义变量:
定义方式:
-v var_name=value
注意:变量名字母大小写敏感的;
示例:
~]# awk -v var='hello' -F: '{print $1"," var}' /etc/passwd
root,hello
bin,hello
daemon,hello
adm,hello
...
常用的ACTION:
2.print:Print the current record,以标准格式输出结果;
格式:
print item1,item2,...
注意:
1) 各item之间需要使用","进行分隔;
2) 输出的各item可以是字符串,可以是数字,可以是当前记录中的字段,可以是变量,可以是awk的表达式;
3) 如果省略了item,则默认的item为$0,即:输出整行;
示例:
~]# awk '{print}' /etc/issue
CentOS release 6.9 (Final)
Kernel \r on an \m
~]# awk '{print $1,$3,$NF}' /etc/issue
CentOS 6.9 (Final)
Kernel on \m
3.printf:Format and print,以特定的格式输出结果;
格式:
printf "FORMAT" item1,item2,...
注意:
1) 必须给出合适的输出格式;
2) 默认不会自动换行,如果想要在输出结果中换行显示,需要显式给出换行控制符号,即:\n;
3) FORMAT中需要为后面的每一个item单独指定一个格式化符号;
常用的FORMAT:%number+字母 标识这么多个数字的字符以%字母格式显示 例:%20s
%c:以ASCII码表中的内容显示字符信息;
%d, %i:显示十进制整数;
%e, %E:以科学计数法来显示数字;浮点类型;
%f, %F:显示十进制数字的浮点形式;
%g, %G:以科学计数法显示浮点数字;
%u:显示无符号十进制数;
%s:显示字符串;
%x, %X:显示无符号的十六进制整数;
%%:显示%;
修饰符:
#[.#]:第一个数字用来控制显示宽度;第二个数字表示小数点的精度;
如:%5s, $8.3f
-:表示采用左对齐方式显示;默认是右对齐;
+:显示数字的正负符号;
示例:
~]# awk -F: '{printf "%20s:%-+5d\n",$1,$3}' /etc/passwd
root:+0
bin:+1
daemon:+2
adm:+3
lp:+4
...
4.操作符:
算术运算操作符:
双目运算符必须用两个数字进行运算:
x+y, x-y, x*y, x/y, x^y, x%y
单目运算符:
-x:将正整数转换为负整数;
+x:将字符串转换为数值;
单目运算符的优先级高于双目运算符
示例:这个示例就是个运算而已后面参数是哪个文件已经无所谓了
~]# awk -F : 'END{print 100^2}' /etc/passwd
字符串操作符:
无任何操作符号时,即为字符串连接操作;
赋值操作符:
=, +=, -=, *=, /=, ^=, %=
++, --
比较操作符:
==, !=, >, >=, <, <=
示例:找到用户UID为1000的并打印出其用户名
~]# awk -F : '$3>=1000{print $1}' /etc/passwd
nfsnobody
模式匹配操作符:
~:操作符左侧的字符串是否能够被右侧的PATTERN所匹配;
!~:操作符左侧的字符串是否不能被右侧的PATTERN所匹配;
示例:
~]# awk -F : '$NF~/bash/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
逻辑运算操作符:
&&
||
!
示例:
~]# awk -F: '$3>=1000&&$3<=1000000{print $0}' /etc/passwd
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
条件表达式:
selector(condition)?if-true-expression:if-false-expression
示例:
~]# awk -F: '{$3>=1000?usertype="Common User":usertype="SuperUser or Sysuser";printf "%20s: %-20s\n",$1,usertype}' /etc/passwd
root: SuperUser or Sysuser
bin: SuperUser or Sysuser
daemon: SuperUser or Sysuser
adm: SuperUser or Sysuser
lp: SuperUser or Sysuser
sync: SuperUser or Sysuser
shutdown: SuperUser or Sysuser
halt: SuperUser or Sysuser
mail: SuperUser or Sysuser
uucp: SuperUser or Sysuser
operator: SuperUser or Sysuser
games: SuperUser or Sysuser
gopher: SuperUser or Sysuser
ftp: SuperUser or Sysuser
nobody: SuperUser or Sysuser
dbus: SuperUser or Sysuser
usbmuxd: SuperUser or Sysuser
rpc: SuperUser or Sysuser
rtkit: SuperUser or Sysuser
avahi-autoipd: SuperUser or Sysuser
vcsa: SuperUser or Sysuser
abrt: SuperUser or Sysuser
rpcuser: SuperUser or Sysuser
nfsnobody: Common User
haldaemon: SuperUser or Sysuser
ntp: SuperUser or Sysuser
apache: SuperUser or Sysuser
saslauth: SuperUser or Sysuser
postfix: SuperUser or Sysuser
gdm: SuperUser or Sysuser
pulse: SuperUser or Sysuser
sshd: SuperUser or Sysuser
tcpdump: SuperUser or Sysuser
named: SuperUser or Sysuser
5.PATTERN部分:
1) empty:空模式,不加区分地处理文件的每一行;
2) [!]/REGEXP/:仅处理[不]能被PATTERN匹配到的行;
示例:
~]# awk -F: '/^r/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
rtkit:x:499:499:RealtimeKit:/proc:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
~]# awk -F: '!/n$/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
3) 关系表达式:
$3>=1000
$NF~/bash/
4) 行域,行范围:
关系表达式的逻辑运算:FNR>=10&&FNR<==20
示例:
~]# awk 'NR>=15&&NR<=20{print NR,$0}' /etc/passwd
15 nobody:x:99:99:Nobody:/:/sbin/nologin
16 dbus:x:81:81:System message bus:/:/sbin/nologin
17 usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
18 rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
19 rtkit:x:499:499:RealtimeKit:/proc:/sbin/nologin
20 avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
/REGEXP1/,/REGEXP2/:
从被REGEXP1匹配的行开始,直到被REGEXP2匹配的行结束,这期间的所有行;凡是属于此类的匹配结果,有多少组就显示多少组;
示例:
~]# awk -F: '/^r/,/^a/{print NR,$0}' /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
18 rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
19 rtkit:x:499:499:RealtimeKit:/proc:/sbin/nologin
20 avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
23 rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
24 nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
25 haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
26 ntp:x:38:38::/etc/ntp:/sbin/nologin
27 apache:x:48:48:Apache:/var/www:/sbin/nologin
5) BEGIN/END模式:
BEGIN{}
仅在开始处理文件中的第一行文本数据之前执行一次的语句块;多用于输出特定格式的表头信息;
示例:
~]# awk -F: 'BEGIN{printf "%20s %10s %20s\n","USERNAME","USERID","SHELL"}NR>=15&&NR<=20{printf "%20s %10s %20s\n",$1,$3,$7}' /etc/passwd
USERNAME USERID SHELL
nobody 99 /sbin/nologin
dbus 81 /sbin/nologin
usbmuxd 113 /sbin/nologin
rpc 32 /sbin/nologin
rtkit 499 /sbin/nologin
avahi-autoipd 170 /sbin/nologin
root: SuperUser or Sysuser
bin: SuperUser or Sysuser
daemon: SuperUser or Sysuser
...
END{}
仅在文本处理完成但awk命令尚未退出时执行一次的语句块;多用于数据信息的汇总;
示例:
~]# awk -F: 'BEGIN{printf "%20s %10s %20s\n","USERNAME","USERID","SHELL"}NR>=15&&NR<=20{printf "%20s %10s %20s\n",$1,$3,$7}END{print "------------------------------------------------------------------\n",NR " users"}' /etc/passwd
USERNAME USERID SHELL
nobody 99 /sbin/nologin
dbus 81 /sbin/nologin
usbmuxd 113 /sbin/nologin
rpc 32 /sbin/nologin
rtkit 499 /sbin/nologin
avahi-autoipd 170 /sbin/nologin
------------------------------------------------------------------
34 users
注意:
1) BEGIN语句块,PATTERN语句块和END语句块的顺序,通常来讲是:
BEGIN{}PATTERN{}END{}
2) BEGIN语句块和END语句块是可选的,但PATTERN语句块必须要给出;
6.常用的ACTIONS
1) 表达式(Expression)
2) 组合语句(Compound Statements)
3) 输入语句(Input Statements)
4) 输出语句(Output Statements)
5) 控制语句(Control Statements)
7.控制语句:
if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
exit [ expression ]
switch (expression) { case value|regex : statement ... [ default: statement ] }
next
1) if ... else:
语法:
if (condition) statement [ else statement ]
使用场景:对awk取得的整行或某个字段做条件判断;
示例:
~]# awk -F: '{if($3>=1000){print "CommonUser:",$1}else{print "Sysuser:",$1}}' /etc/passwd
Sysuser: root
Sysuser: bin
Sysuser: daemon
Sysuser: adm
...
~]# awk '/^[^#]/{if(NF==6){print}}' /etc/fstab
/dev/mapper/VolGroup-lv_root / ext4 defaults 1 1
UUID=f1dc8baa-7d08-4d88-943c-339bae3704f9 /boot ext4 defaults 1 2
/dev/mapper/VolGroup-lv_home /home ext4 defaults 1 2
/dev/mapper/VolGroup-lv_swap swap swap defaults 0 0
tmpfs /dev/shm tmpfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
sysfs /sys sysfs defaults 0 0
proc /proc proc defaults 0 0
/dev/raid_vg/raid_lv /userhome ext4 defaults 0 0
分析磁盘上各个文件系统的空间利用率:
~]# df -h | awk -F% '/^\/dev/{print $1}' | awk '{if($NF>=80){print $1}}'
/dev/sr0
2) while循环:
语法:
while (condition) statement
使用场景:
a.对一行内的多个字段逐一做相同或类似的操作处理时使用;
b.对数组中的各数组元素做遍历处理时使用;
while循环的特点:条件为真,进入循环;一旦条件为假,则退出循环;
示例:判断字符串长度
~]# awk '{i=1;while(i<=NF){print $i,length($i);i++}}' testfile
The 3
first 5
day 3
when 4
I 1
started 7
my 2
middle 6
school 6
life, 5
I 1
felt 4
so 2
lonely, 7
because 7
I 1
didn’t 6
know 4
anyone 6
who 3
was 3
around 6
me. 3
3) do ... while语句:
语法:
do statement while (condition)
意义:与while循环相同,但statement语句段至少被执行一次;
4) for循环:
语法:
for (expr1; expr2; expr3) statement
expr1:variable assignment,变量赋初值;
expr2:circle condition,循环条件判断;
expr3:interation process,变量值修正方法;
示例:
~]# awk '{for(i=1;i<=NF;i++){print $i,length($i)}}' testfile
The 3
first 5
day 3
when 4
I 1
started 7
my 2
middle 6
school 6
life, 5
I 1
felt 4
so 2
lonely, 7
because 7
I 1
didn’t 6
know 4
anyone 6
who 3
was 3
around 6
me. 3
for (var in array) statement
5) switch ... case语句
语法:
switch (expression) { case value|regex:statement;case value2|regex2:statement;... [ default: statement ] }
使用场景:
用于进行字符串比较判断;
6) break和continue语句:
break [n]
continue
注意:其使用场景是行内多个字段间做循环时的循环控制方式;
示例:
~]# awk '{for(i=1;i<=NF;i++){if(length($i)<5){continue}else{print $i,length($i)}}}' testfile
first 5
started 7
middle 6
school 6
life, 5
lonely, 7
because 7
didn’t 6
anyone 6
around 6
7) next语句:
在awk处理数据时,提前结束对当前行的处理,而直接开始处理下一行;
示例:
~]# awk -F: '{if($3%2==1){next}else{print $1,$3}}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
uucp 10
games 12
ftp 14
rpc 32
avahi-autoipd 170
nfsnobody 65534
haldaemon 68
ntp 38
apache 48
saslauth 498
gdm 42
sshd 74
tcpdump 72
或者用下面这种方式也是一样的
~]# awk -F: '{if($3%2==1)next;print $1,$3}' /etc/passwd
8.数组——Array
用户自定义的数组,一般使用关联数组:array_name[index_expression]
注意:
1) index_expression可以使用任意的字符串,但字符串必须放在双引号中;
2) 支持弱变量数组,即:如果某数组元素事先不存在,当引用该元素时,awk会自动创建此元素,并为此元素赋"空字符串"作为其初始值;
示例:
~]# awk 'BEGIN{name["leader"]="zhangsan";name["mem1"]="lisi";name["mem2"]="bob";print "Leader:",name["leader"],"Member:",name["mem1"],name["mem2"]}'
Leader: zhangsan Member: lisi bob
~]# awk 'BEGIN{name["leader"]="zhangsan";name["mem1"]="lisi";name["mem2"]="bob";for(i in name){print name[i]}}'
lisi
bob
zhangsan
查看当前系统上所有服务的不同TCP状态的连接数量的统计:
~]# netstat -nalt | awk '/^tcp\>/{state[$NF]++}END{for(stat in state){printf "%15s: %-10d\n",stat,state[stat]}}'
ESTABLISHED: 1
LISTEN: 12
用于统计本服务器web站点的每个用户的请求数值:
~]# awk '{ipaddr[$1]++}END{for(ip in ipaddr){print ip,ipaddr[ip]}}' /var/log/httpd/access_log
用于统计本服务器web站点的UV值:
~]# awk '{ipaddr[$1]++}END{for(ip in ipaddr){print ip,ipaddr[ip]}}' /var/log/httpd/access_log | wc -l
9.函数:
内建函数:
数值函数:Numeric Functions
rand():返回一个介于0到1之间的随机数;
sqrt():对于指定的数值进行开二次方;
字符串函数:String Functions
length():计算给定字符串的长度;
gsub(r, s [, t]):以r表示的模式来查找t表示的字符串中能够被匹配的内容,并将所有出现的内容替换成s表示的内容;
split(s, a [, r [, seps] ]):以seps作为分隔符,利用r表示的模式进行匹配,将s代表的字符串分割之后,保存在a表示的数组中;
自定义函数:
function name(parameter list) { statements }