目录
Linux之AWK
定义
awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势
语法格式
awk` `[options] ``'program'` `var=value ``file``…``awk` `[options] -f programfile var=value ``file``…``awk` `[options] ``'BEGIN{ action;… } pattern{ action;… } END{ action;… }'` `file` `...
常用选项
-F fs --- fs指定输入分隔符号,fs可以使字符串或正则表达式,如-F
-v var=value --- 复制一个定义用户变量,将外部变量传递给awk
-f scripfile --- 从脚本文件中读取awk
awk 变量
变量 --- 内置和自定义变量,每个变量前加-v 命令选项
内置变量
格式
-
FS --- 输入字段分隔符,默认为空白字符
-
OFS --- 输出字段分隔符,默认为空白字符
-
RS --- 输入记录分隔符,指定输入时的换行符,原换行符仍有效
-
ORS ---输出记录分隔符,输出时用指定符号代替换行符
-
NF --- 字段数量,共有多少字段, $NF引用最后一列,$(NF-1)引用倒数第2列
-
NR --- 行号,后可跟多个文件,第二个文件行号继续从第一个文件最后行号开始
-
FNR --- 各文件分别计数, 行号,后跟一个文件和NR一样,跟多个文件,第二个文件行号从1开始
-
FILENAME --- 当前文件名
-
ARGC --- 命令行参数的个数
-
ARGV --- 数组,保存的是命令行所给定的各参数,查看参数
案例
(1)编写一个文本名字叫做awkdemo,里面内容如下
hello:world
linux:redhat:lalala:hahaha
along:love:youou
(2)FS指定输入分隔符
awk -v FS=':' '{print $1,$2}' awkdemo
(3)OFS指定输出分隔符
awk -v FS=':' -v OFS='---' '{print $1,$2}' awkdemo
(4)RS指定输入的换行符
awk -v RS=':' '{print $1,$2}' awkdemo
(5)显示倒数第2列
awk -F: '{print $(NF-1)}' awkdemo
(6)显示行号
root@ubuntu:/test# awk '{print NR}' awkdemo
(7)显示当前文件名
awk '{print FILENAME}' awkdemo
自定义变量
(1)-v var = value
1.先定义变量,后执行动作print
awk -v name="along" -F: '{print name":"$0}'
2.在执行动作print后定义变量
awk -F: '{print name":"$0;name="along"}' awkdemo
(2)在program中直接定义
可以把执行的动作放在脚本中,直接调用脚本-f
root@ubuntu:/test# cat awk.txt
{name="along";print name,$1}
root@ubuntu:/test# awk -F: -f awk.txt awkdemo
printf命令
比print更强大
语法格式
(1)格式化输出
printf` `"FORMAT"``, item1,item2, ...
- 必须指定FORMAT
- 不会自动换行,需要显式给出换行控制符,\n
- FORMAT 中需要分别为后面每个item 指定格式符
(2)格式符 --- 与item对应
%c: --- 显示字符的ASCII码
%d, %i: --- 显示十进制整数
%e, %E --- 显示科学计数法数值
%f --- 显示为浮点数,小数 %5.1f,带整数、小数点、整数共5位,小数1位,不够用空格补上
%g, %G --- 以科学计数法或浮点形式显示数值
%s --- 显示字符串;例:%5s最少5个字符,不够用空格补上,超过5个还继续显示
%u --- 无符号整数
%% --- 显示% 自身
(3)修饰符 --- 放在 %c[/d/e/f...]之间
#[.#] --- 第一个数字控制显示的宽度;第二个# 表示小数点后精度,%5.1f
- --- 左对齐(默认右对齐) %-15s
+ --- 显示数值的正负符号 %+d
案例
模式匹配符
查询以/dev开头的磁盘信息
df -h |awk -F: '$0 ~ /^\/dev/'
只显示磁盘使用状况和磁盘名
df -h |awk '$0 ~ /^\/dev/{print $(NF-1)"---"$1}'
查找磁盘大于40%的
df -h |awk '$0 ~ /^\/dev/{print $(NF-1)"---"$1}' |awk -F% '$1 > 40'
逻辑操作符
awk -F: '$3>=0 && $3<=1000 {print $1,$3}' /etc/passwd
wk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd
awk -F: '!($3==0) {print $1}' /etc/passwd
awk -F: '!($0 ~ /bash$/) {print $1,$3}' /etc/passwd
条件表达式(三目表达式)
awk -F: '{$3 >= 1000?usertype="common user":usertype="sysadmin user";print usertype,$1,$3}' /etc/passwd
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{} :仅在文本处理完成之后执行
案例
awk -F: '{print $1}' awkdemo
awk -F: '/along/{print $1}' awkdemo
awk -F: '1{print $1}' awkdemo
awk -F: 'BEGIN{print "第一列"}{print $1} END{print "结束"}' awkdemo
案例 --- 筛选给定时间范围内的日志
grep/sed/awk用正则去筛选日志时,如果要精确到小时、分钟、秒,则非常难以实现,但是awk提供了mktime()函数,它可以将时间转换成epoch时间值
# 2019-11-10 03:42:40转换成epoch为1970-01-01 00:00:00
$ awk 'BEGIN{print mktime("2019 11 10 03 42 40")}'
将时间转换成一个时间戳
借此,可以取得日志中的时间字符串部分,再将它们的年、月、日、时、分、秒都取出来,然后放入mktime()构建成对应的epoch值。因为epoch值是数值,所以可以比较大小,从而决定时间的大小
第一种时间格式
将2019-11-10T03:42:40+08:00
格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志
BEGIN{
#选择要筛选日志的时间,将其时间构建成epoch值
which_time = mktime("2019 11 10 03 42 40")
}
{
# 取出日志中的日期时间字符串部分
match($0,"^.*\\[(.*)\\].*",arr)
# 将日期时间字符串转换为epoch值
tmp_time = strptime1(arr[1])
# 通过epoch值来比较时间大小并打印
if(tmp_time > which_time){print}
}
# 构建的时间字符串格式为:"2019-11-10T03:42:40+08:00"
function strptime1(str,arr,Y,M,D,H,m,S){
patsplit(str,arr,"[0-9]{1,4}")
#patsplit是pattern split的意思,是将pattern匹配成功的部分放到队列里,默认以FPAT为匹配正则
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))
#sprintf是将输出保存在一个字符串里- sprintf(fmt, expr-list)
}
第二种时间格式
将10/Nov/2019:23:53:44+08:00
格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志
BEGIN{
# 要筛选什么时间的日志,将其时间构建成epoch值
which_time = mktime("2019 11 10 03 42 40")
}
{
# 取出日志中的日期时间字符串部分
match($0,"^.*\\[(.*)\\].*",arr)
# 将日期时间字符串转换为epoch值
tmp_time = strptime2(arr[1])
# 通过比较epoch值来比较时间大小
if(tmp_time > which_time){
print
}
}
# 构建的时间字符串格式为:"10/Nov/2019:23:53:44+08:00"
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]
}