语法格式
awk [参数] [处理内容] [操作对象]
常用命令选项
-F fs:fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:
-v var=value:赋值一个用户定义变量,将外部变量传递给awk
-f scripfile:从脚本文件中读取awk命令
内置变量
- FS :输入字段分隔符,默认为空白字符
- OFS :输出字段分隔符,默认为空白字符
- RS :输入记录分隔符,指定输入时的换行符,原换行符仍有效
- ORS :输出记录分隔符,输出时用指定符号代替换行符
- NF :字段数量,共有多少字段, $ NF引用最后一列,$(NF-1)引用倒数第2列
- NR :行号,后可跟多个文件,第二个文件行号继续从第一个文件最后行号开始
- FNR :各文件分别计数, 行号,后跟一个文件和NR一样,跟多个文件,第二个文件行号从1开始
- FILENAME :当前文件名
- ARGC :命令行参数的个数
- ARGV :数组,保存的是命令行所给定的各参数,查看参数
演示
OFS——指定输出分隔符
在awkdemo文件中,字段之间的分隔符是空格,使用OFS,指定输出的分隔符为—
[root@bogon ~]# cat awkdemo
hello world
[root@bogon ~]# awk -v FS=' ' -v OFS='---' '{print $1,$2}' awkdemo
hello---world
FNR——统计文件行数
[root@bogon ~]# awk '{print FNR}' awkdemo
1
[root@bogon ~]# vi awkdemo
hello world
sdassasad
dasdkqjwlk
nzxhcozxjhclk
dnaskjdnsa
[root@bogon ~]# awk '{print FNR}' awkdemo
1
2
3
4
5
awk PATTERN匹配
格式:
PATTERN:根据pattern 条件,过滤匹配的行,再做处理
(1)如果未指定:空模式,匹配每一行
(2)/regular expression/ :仅处理能够模式匹配到的行,正则,需要用/ / 括起来
(3)relational expression:关系表达式,结果为“真”才会被处理
真:结果为非0值,非空字符串
假:结果为空字符串或0值
(4)line ranges:行范围
startline(起始行),endline(结束行):/pat1/,/pat2/ 不支持直接给出数字,可以有多段,中间可以有间隔
(5)BEGIN/END 模式
BEGIN{}: 仅在开始处理文件中的文本之前执行一次
END{} :仅在文本处理完成之后执行
演示:
打印awkdemo文件中的第一个字段
[root@bogon ~]# cat awkdemo
hello world
linux:sentos
[root@bogon ~]# awk -F: '{print $1}' awkdemo
hello world
linux
在执行文件中的文本内容之前执行代码 BEGIN{print “第一列”}
执行之后执行 END{print “结束”}’
[root@bogon ~]# awk -F: 'BEGIN{print "第一列"}{print $1} END{print "结束"}' awkdmo
第一列
hello world
linux
结束
awk控制语句—if-else判断:
语法:
if(condition){statement;…}[else statement] 双分支
if(condition1){statement1}else if(condition2){statement2}else{statement3} 多分支
对/etc/passwd文件中第三个字段进行条件判断,打印第一,第三个字段
[root@bogon ~]# awk -F: '{if($3>10 && $3<1000)print $1,$3}' /etc/passwd
operator 11
games 12
ftp 14
nobody 99
systemd-network 192
dbus 81
polkitd 999
sshd 74
postfix 89
awk控制语句—while循环:
语法:
while``(condition){statement;…}
以:为分隔,显示每一行的长度大于6的单词和其长度
[root@bogon ~]# awk -F: '{i=1;while(i<=NF) {if(length($i)>=6){print $i,length($i)}; i++}}' awkdemo
hello world 11
sentos 6
redhat 6
案例:筛选给定时间范围内的日志
grep/sed/awk用正则去筛选日志时,如果要精确到小时、分钟、秒,则非常难以实现。
但是awk提供了mktime()函数,它可以将时间转换成epoch时间值。epoch值是数值,可以用来比较大小
awk 'BEGIN{print mktime("2019 11 10 03 42 40")}'
BEGIN{
which_time = mktime("2019 11 10 03 42 40")
}
{
match($0,"^.*\\[(.*)\\].*,arr") //取出日志中的字符串
tmp_time = strptime1(arr[1]) //将字符串转换为epoch值
if(tmp_time > which_time){print} //通过epoch值来比较大小
}
functiom steptime1(str,arr,Y,M,D,h,m,s){
patsplit(str,arr,"[0-9]{1,4}")
Y=arr[1]
M=arr[2]
D=arr[3]
h=arr[4]
m=arr[5]
s=arr[6]
return mktime(sprintf(%s %s %s %s %s %s,Y,M,D,h,m,s))
}
构建时间字符串格式
部分日志时间可能出现,月份为英文的情况因为英文部分无法比较大小,需要将月份为英文的字段转换为数字,进行比较
function strptime2(str,dt_str,arr,Y,M,D,H,m,S) {
dt_str = gensub("[/:+]"," ","g",str)
# dt_sr = "10 Nov 2019 23 53 44 08 00"
split(dt_str,arr," ")
Y=arr[3]
M=mon_map(arr[2])
D=arr[1]
H=arr[4]
m=arr[5]
S=arr[6]
return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}
function mon_map(str,mons){
mons["Jan"]=1
mons["Feb"]=2
mons["Mar"]=3
mons["Apr"]=4
mons["May"]=5
mons["Jun"]=6
mons["Jul"]=7
mons["Aug"]=8
mons["Sep"]=9
mons["Oct"]=10
mons["Nov"]=11
mons["Dec"]=12
return mons[str]
}