Linux awk

[TOC]

awk介绍

awk是Linux三剑客中功能最强大,使用最复杂的命令。强大到awk都成为一种语言了,它适合文本处理和报表生成,其语法较为常见,借鉴了某些语言的一些精华,如 C 语言等。在 linux 系统日常处理工作中,发挥很重要的作用,掌握了 awk将会使你的工作变的高大上。 awk 是三剑客的老大,利剑出鞘,必会不同凡响。

awk基本用法

输入awk就可以看到awk简要帮助,下面就结合我自己查找资料和使用之后的一个总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
awk基本用法
awk [options] 'program' var=value file…
awk [options] -f programfile var=value file…
'program' = 'BEGIN{ action;… } pattern{ action;… } END{ action;… }'

options(选项):
-f progfile --file=progfile #从文件中读取program语法
-F fs --field-separator=fs #字段分隔符,默认空格
-v var=val --assign=var=val #自定义变量
#官方简单例子,将/etc/passwd以冒号":"分隔,将得到的的第一列打印
#gawk -F: '{ print $1 }' /etc/passwd
root
bin
daemon
...

1.分隔符.域和记录

awk执行时,参数-F 的值就是分隔符,不写默认为空格.

分割出来的字段就是域,从前往后一次标记为$1,$2,$3...$n 称为域标示.$0为所有域.

文件的每一行称为一条记录.

2.awk语法

1
2
3
4
5
6
program: 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' 
pattern和action:
pattern部分决定动作语句何时触发及触发事件(BEGIN,END 也是属于pattern中的特殊部分吧)
action statements对数据进行处理,放在{}内指明
(print, printf)

3.awk工作过程

awk的工作过程:
第一步: 执行 BEGAIN{action;…} 语句块中的语句,如果没有就跳过此步骤.
第二步: 从文件和标准输入读取一行(就是一条记录),然后按照pattern中匹配中了就执行后面的 { action;… } ,然后读取下一条记录匹配执行,重复这个过程知道文件全部读取完毕.
第三步: 执行 END{ action;… } 语句块中的语句,如果没有就跳过此步骤.

注意:

pattern是可选的,省略action,则默认执行print $0 的操作.但是呢,pattern和{ action;} 不能同时省略.

1
2
3
4
5
6
7
8
9
10
11
12
13
[yhu@master model]$awk /root/{print} /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[yhu@master model]$awk /root/ /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[yhu@master model]$awk {print} /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
...

4.print格式

print格式:print item1, item2, …
要点:

​ (1) 逗号分隔符
​ (2) 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式,输出的各项item 以空格隔开(默认)
​ (3) 如省略item,相当于print $0
示例:

1
2
3
4
awk –F: ‘{print $1}’ /etc/passwd  #冒号分割,每一条记录打印第一个域
awk –F: ‘{print $0}’ /etc/passwd #冒号分割,每一条记录打印全部域
awk –F: ‘{print $1”\t”$3}’ /etc/passwd #冒号分割,每一条记录打印第一个域一个制表符再打印第三个域
tail –3 /etc/fstab|awk ‘{print $2,$4}’ #传入/etc/fstab后三行,空格分割,每一条记录打印第二个域和第四个域

5.awk变量

变量分为内置变量和自定义变量.

内置变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
FS:输入字段分隔符,默认为空白字符 filed separator
awk -v FS=':' '{print $1,$3,$7}' /etc/passwd
awk –F: '{print $1,$3,$7}' /etc/passwd
OFS:输出字段分隔符,默认为空白字符 output filed separator
awk -v FS=':' -v OFS='-' '{print $1,$3,$7}' /etc/passwd
RS:输入记录分隔符,指定输入时的换行符,原换行符仍有效 record separator
awk -v RS=' ' '{print }' /etc/passwd
ORS:输出记录分隔符,输出时用指定符号代替换行符 output record separator
awk -v RS=' ' -v ORS='###' '{print }' /etc/passwd
NF:字段数量 number filed
awk -F: '{print NF}' /etc/passwd ,引用内置变量不用$
awk -F: '{print $(NF-1)}' /etc/passwd
NR:行号 number row
awk '{print NR}' /etc/fstab;
awk END'{print NR}' /etc/fstab
awk 'BEGIN{print 0} {print NR} END{print NR}' /etc/fstab
FNR:各文件分别计数,行号 file number row
awk '{print FNR}' /etc/fstab /etc/inittab
FILENAME:当前文件名
awk '{print FILENAME}' /etc/fstab
ARGC:命令行参数的个数 arguement count
awk '{print ARGC}' /etc/fstab /etc/inittab
awk 'BEGIN{print ARGC}' /etc/fstab /etc/inittab
ARGV:数组,保存的是命令行所给定的各参数 arguement value
awk 'BEGIN {print ARGV[0]}' /etc/fstab /etc/
--> awk
awk 'BEGIN {print ARGV[1]}' /etc/fstab /etc/inittab
--> /etc/fstab
自定义变量(区分字符大小写)
(1) -v var=value
(2) 在program中直接定义
示例:
awk -v test='hello gawk' '{print test}' /etc/fstab
awk -v test='hello gawk' 'BEGIN{print test}'
awk 'BEGIN{test="hello,gawk";printtest}'
awk –F:‘{sex=“male”;print$1,sex,age;age=18}’ /etc/passwd

cat awkscript
{print script,$1,$2}
awk -F: -f awkscriptscript=“awk” /etc/passwd

自定义变量

自定义变量可以在两个地方定义:

1
2
(1) -v var=value
(2) 在program中直接定义

示例:

1
2
3
4
5
6
7
awk -v test='hello gawk' '{print test}' /etc/fstab
awk 'BEGIN{test="hello,gawk";print test}'
awk –F: '{sex="male";print$1,sex,age;age=18}' /etc/passwd

cat awkscript
{print script,$1,$2}
awk -F: -f awkscript script=“awk” /etc/passwd

6.printf

printf : 格式化输出,输出各种你想要的格式.(ascii,十进制整数,十六进制,无符号,小数…)

格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
printf “FORMAT”, item1, item2, ...
FORMAT格式符:与item一一对应
%c: 显示字符的ASCII码
%d, %i: 显示十进制整数
%e, %E:显示科学计数法数值
%f:显示为浮点数
%g, %G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%: 显示%自身
FORMAT修饰符:
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
-: 左对齐(默认右对齐)%-15s
+:显示数值的正负符号%+d

注意:

1
2
3
4
(1) 必须指定FORMAT,记得加引号
(2) 不会自动换行,需要显式给出换行控制符,\n
(3) FORMAT中需要分别为后面每个item指定格式符
(4) 是printf哦,不是print

示例:

1
2
3
4
5
6
7
awk -F: '{printf "%s",$1}' /etc/passwd
awk -F: '{printf "%s\n",$1}' /etc/passwd
awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
awk -F: '{printf "Username: %s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %15s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %-25sUID: %d\n",$1,$3}' /etc/passwd

7.操作符

awk操作符就多了,有算数操作符,字符串操作符,赋值操作符,比较操作符,模式匹配符,逻辑操作符.是不是在学一门语言啊?O(∩_∩)O哈哈~ 其实每一个都很简单,就一般程序操作符基本一样.来看下

算数操作符

1
2
3
x+y, x-y, x*y, x/y, x^y, x%y
-x: 转换为负数
+x: 转换为数值

字符串操作符

在没有符号的操作符,字符串就连接起来了.

赋值操作符

1
2
=, +=, -=, *=, /=, %=, ^=
++, --

模式匹配符

1
2
3
4
5
6
~:左边是否和右边匹配包含
!~:是否不匹配
awk -F: '$0 ~ /root/{print $1}' /etc/passwd
awk '$0~"^root"' /etc/passwd
awk '$0 !~ /root/' /etc/passwd
awk -F: '$3==0' /etc/passwd

逻辑操作符

1
2
3
4
5
6
7
8
与&&
或||
非!
示例:
awk -F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd
awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd
awk -F: '!($3==0){print $1}' /etc/passwd
awk -F: '!($3>=500) {print $3}' /etc/passwd

函数调用

1
function_name(argu1, argu2, ...)

条件表达式

1
selector?if-true-expression:if-false-expression

示例:

1
2
3
4
5
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf"%20s: %-s\n",$1,usertype}' /etc/passwd

# selector --> ($3>=1000)
# if-true-expression --> (usertype="Common User")
# if-false-expression --> (usertype="Sysadmin or SysUser")

8.awk pattern

pattern:根据pattern条件,过滤匹配的行,在做处理

(1)如果未指定:空模式,匹配每一行
(2) /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来

1
2
awk '/^UUID/{print $1}' /etc/fstab
awk '!/^UUID/{print $1}' /etc/fstab

(3) relational expression: 关系表达式,结果为“真”才会被处理

1
2
3
4
5
6
7
8
真:结果为非0值,非空字符串
假:结果为空字符串或0值
awk -F: 'i=1;j=1{print i,j}' /etc/passwd
awk '!0' /etc/passwd; awk '!1' /etc/passwd
awk -F: '$3>=1000{print $1,$3}' /etc/passwd
awk -F: '$3<1000{print $1,$3}' /etc/passwd
awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd

(4) line ranges:行范围

startline,endline:/pat1/,/pat2/不支持直接给出数字格式

1
2
awk -F: '/^root\>/,/^nobody\>/{print $1}' /etc/passwd
awk -F: '(NR>=10&&NR<=20){print NR,$1}' /etc/passwd

(5) BEGIN/END模式

BEGIN{}: 仅在开始处理文件中的文本之前执行一次
END{}:仅在文本处理完成之后执行一次

示例:

1
2
3
4
5
6
7
8
9
10
awk -F: 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "end file"}' /etc/passwd
awk -F: '{print "USER USERID“;print $1":"$3} END{print "end file"}' /etc/passwd
awk -F: 'BEGIN{print " USER UID \n---------------"}{print $1,$3}' /etc/passwd
awk -F: 'BEGIN{print "USER UID \n--------------------------"} {printf "%-20s%s\n" ,$1,$3} END{print "========================="}' /etc/passwd
seq 10 |awk 'i=0'
seq 10 |awk 'i=1'
seq 10 | awk 'i=!i'
seq 10 | awk '{i=!i;print i}'
seq 10 | awk '!(i=!i)'
seq 10 |awk -v i=1 'i=!i'

9.awk action

常用的action分类
(1) Expressions:算术,比较表达式等
(2) Control statements:if, while等
(3) Compound statements:组合语句
(4) input statements
(5) output statements:print等

控制语句

action中的控制语句是强大的,现在挑几个重要且常用说:

1
2
3
4
5
6
7
8
9
10
11
{ statements;… } 组合语句
if(condition) {statements;…}
if(condition) {statements;…} else {statements;…}
while(conditon) {statments;…}
do {statements;…} while(condition)
for(expr1;expr2;expr3) {statements;…}
break
continue
delete array[index]
delete array
exit

if-else

语法:

1
2
3
if(condition){statement;…}[else {statement;…}]
if(condition1){statement1}else if(condition2){statement2}
else{statement3}

使用场景:对awk取得的整行或某个字段做条件判断
示例:

1
2
3
4
5
6
7
8
9
awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
awk -F: '{if($3>=1000) {printf"Common user: %s\n",$1} else {printf"root or Sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($3>=1000) printf "Common user: %s\n",$1;
else printf "root or Sysuser: %s\n",$1}' /etc/passwd
df -h|awk -F% '/^\/dev/{print $1}'|awk '$NF>=80{print $1,$5}‘
awk 'BEGIN{ test=100;if(test>90){print "very good"}
else if(test>60){ print "good"}else{print "no pass"}}'

while

语法:while(condition){statement;…}

条件“真”,进入循环;条件“假”,退出循环
使用场景:
对一行内的多个字段逐一类似处理时使用
对数组中的各元素逐一处理时使用
示例:

1
2
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i); i++}}' /etc/grub2.cfg
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=10) {print $i,length($i)}; i++}}' /etc/grub2.cfg

do-while

语法:do {statement;…} while(condition)
意义:无论真假,至少执行一次循环体
示例:

1
awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'

for

语法:for(expr1;expr2;expr3) {statement;…}

特殊用法:能够遍历数组中的元素;
语法:for(varin array) {for-body}

1
2
3
4
5
6
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
性能比较:
time(awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}')
time(total=0;for i in {1..10000};do total=$(($total+i));done;echo $total)
time(for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
time(seq -s "+" 10000|bc)

switch

语法:

1
switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2;...; default: statementn}

break和continue

1
2
awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==66)break;sum+=i}print sum}'

函数

数值处理:
rand():返回0和1之间一个随机数

1
awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'

字符串处理:
length([s]):返回指定字符串的长度
sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为s

1
echo "2008:08:08 08:08:08" | awk 'sub(/:/,“-",$1)'

gsub(r,s,[t]) :对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容

1
echo "2008:08:08 08:08:08" | awk 'gsub(/:/,“-",$0)'

split(s,array,[r]) :以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

1
netstat-tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (iin count) {print i,count[i]}}'

自定义函数

格式:

1
2
3
4
function name ( parameter, parameter, ... ) {
statements
return expression
}

示例:

1
2
3
4
5
6
7
#cat fun.awk
function max(v1,v2) {
v1>v2?var=v1:var=v2
return var
}
BEGIN{a=3;b=2;print max(a,b)}
#awk –f fun.awk

调用shell命令

system命令

空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用””引用起来。

1
2
awk BEGIN'{system("hostname") }'
awk 'BEGIN{score=100; system("echo your score is " score) }'

10.调用脚本

将awk程序写成脚本,直接调用或执行

示例:

1
2
3
4
5
6
7
8
9
10
11
12
#cat f1.awk
if($3>=1000)print $1,$3}

#awk -F: -f f1.awk /etc/passwd

#cat f2.awk
#!/bin/awk –f
#this is a awk script
{if($3>=1000)print $1,$3}

#chmod +x f2.awk
#f2.awk –F: /etc/passwd

测试:

1
2
3
4
1、统计/etc/fstab文件中每个文件系统类型出现的次数
2、统计/etc/fstab文件中每个单词出现的次数
3、提取出字符串Yd$C@M05MB%9&Bdh7dq+YVixp3vpw中的所有数字
4、解决DOS攻击生产案例:根据web日志或者或者网络连接数,监控当某个IP并发连接数或者短时内PV达到100,即调用防火墙命令封掉对应的IP,监控频率每隔5分钟。防火墙命令为:iptables -A INPUT -s IP -j REJECT