awk 示例
2016-02-17 by dongnan
输入驱动
awk是输入驱动的:
file a.txt b.txt
a.txt: ASCII text
b.txt: empty
文件有1行内容则输出1行
awk '{print}' a.txt
print this line
空文件没有输出
awk '{print}' b.txt
# 什么都没有输出
程序设计模型
awk 由主输入(mian input
)循环组成,主输入循环执行次数由输入的行数决定。
awk 允许编写两个特殊的过程,BEGIN
与END
分别对应主输入循环执行前和主输入循环执行后,BEGIN
和END
过程是可选的:
# 计算第一列的总和
find -name "*.gz" -exec du {} \; | awk 'BEGIN{sum=0}{sum+=$1}END{print sum/1024" MB"}'
awk 由3部分组成,处理前,处理中,处理后。
在主输入循环中,指令被写成一些列的模式/动作过程,模式是用于匹配入行的规则,以确定动作是否应用这些输入行:
# 语法
awk '/pattern/ {action}'
模式匹配
当 awk 读入一行时,将匹配每个模式的规则,只有与一个特定的模式相匹配的输入行才能成为操作对象,如果没有指定操作,将打印匹配输入的行。
awk '/^root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
匹配空行
echo "" | awk '/^$/ {print "this is blank line"}'
this is blank line
匹配数字
echo "123" | awk '/[0-9]+/ {print "this is an integer"}'
this is an integer
匹配字符
echo "abc" | awk '/[A-Za-z]+/ {print "this is a string"}'
this is a string
注释
awk 注释以字符 #
开始,以换行符结束:
echo "123" | awk '/[0-9]/;#这里是注释'
123
记录和字段
awk 假设输入是有结构的,最简单的是将空格或者制表符分隔的单词作为字段,连续的两个或者多个空格或制表符会被当做一个分隔符。
字段引用
awk 使用$
符号来操作字段,例如$1
表示第1个字段,$2
表示第2个字段等等,$0
表示整个输入行(记录)。
print
语句使用,
逗号分隔参数,逗号输出的分隔符为空格。
awk -F: '{print $1,$2}' /etc/passwd | head -n2
root x
bin x
可以使用任何计算值为整数的表达式来表示一个字段
awk -F: '{print $(NF-2)}' /etc/passwd | head -n2
root
daemon
以解析nginx日志为例,日志内容如下:
tail -n1 access.log
150.138.216.81 - - [10/Dec/2018:14:45:52 +0800] "POST /works/MemberLogin.asp HTTP/1.1" 404 95 "-"
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; .NET CLR 1.1.4322)" 101.227.1.202 0.002
最后一列
tail -n1 access.log | awk '/"Mozilla\/4.0 \(compatible;/ {print $NF}'
0.002
倒数第2列
tail -n1 access.log | awk '/"Mozilla\/4.0 \(compatible;/ {print $(NF-1)}'
101.227.1.203
依次类推,倒数第3列
tail -n1 access.log | awk '/"Mozilla\/4.0 \(compatible;/ {print $(NF-2)}'
1.1.4322)"
分隔符
命令行下 -F
选项,用于改变分隔符,默认的分隔符是空格。通过定义系统变量FS
可以改变字段分隔符,变量FS
需要提前在BEGIN
过程定义。
awk 'BEGIN{FS=":"} {print $(NF-2)}' /etc/passwd | head -n2
root
daemon
也可以使用正则表达式,指定几个字符作为分隔符。
awk 'BEGIN{FS="[,:\t]"} {print $(NF-2)}' /etc/passwd | head -n2
root
daemon
匹配字段
awk -F: '/bash/ {print $1,$NF}' /etc/passwd
root /bin/bash
dongnan /bin/bash
#~ 运算符,用于匹配正则表达式
awk -F: '$1 ~ /root/ {print $1,$NF}' /etc/passwd
root /bin/bash
#! 运算符,表达式值取反
awk -F: '$1 !~ /root/ {print $1,$NF}' /etc/passwd |head -n1
daemon /usr/sbin/nologin
表达式
表达式由数字和字符串常量,变量,操作符,函数,正则表达式组成。
匹配大于1
秒的mysql
慢查询记录;
awk '/Query_time/ && $3 > 1' slow_queries.log
# Query_time: 1.007705 Lock_time: 0.000033 Rows_sent: 769 Rows_examined: 769
# Query_time: 12.118960 Lock_time: 0.000248 Rows_sent: 0 Rows_examined: 0
# Query_time: 1.253649 Lock_time: 0.000043 Rows_sent: 793 Rows_examined: 793
- '这里都是表达式'
/匹配的正则或字符串/
-&& 逻辑与操作符
$3 > 1 表达式
常量
有两种类型:字符串型和数字型("abc"
、123
),字符串必须由引号括起来;
变量
变量是引用值的标识符,定义变量只需要为它定义一个名字并将数据赋值给它即可,变量区分大小写变量不必初始化,awk 自动初始化空字符串或数字0
等号 =
是赋值操作符,空格
是连接字符串的操作符
echo "" | awk '{x=1;y="hello" "world";print y,x}'
helloworld 1
关系表达式
关系操作符和布尔操作符用于在两个表达式之间进行比较;
关系运算符
< #小于
<= #小于等于
> #大于
>= #大于等于
!= #不等的
== #相等的
~ #匹配
!~ #不匹配
关系表达式可用在模式中来控制特殊的操作
$1==month {print $NF}
正则表达式经常用斜杠包围,这经常被作为正则表达式常量
/Failed/ { ++F[$(NF-3)]}
然而也不限于正则表达式常量,当使用关系操作符时,右边的表达式可以是awk 中的任意表达式。awk 将他作为一个字符串处理
$6 ~ /Failed/ {print $(NF-3)}
布尔运算符
|| #逻辑或
&& #逻辑与
! #逻辑非
可以使用圆括号来改变优先级
($1=="Jan" && $2==19) && /Failed/ {++F[$(NF-3)]}
!
操作符 结合 in
操作符非常有用,可用来判断某个下标是否在数组中
!(i in F)
系统变量
awk 中有许多系统变量或内置变量。
系统变量FS
定义字段分隔符,它的默认值为一个空格,FS变量可以被设置为任何单独的字符或正则表达式。
与FS
等效的是OFS
,它表示输出分隔符,默认值也是一个空格。
系统变量NF
,为当前输入记录的字段个数,改变NF
的值会有副作用,$NF
代表最后一个Field
(列) 不要与 NF
搞混了。
系统变量FILENAME
,为当前输入文件的名称,当应用多个输入文件时,系统变量FNR被用来表示与当前输入文件相关记录行。
系统变量RS
,为记录分隔符它的默认值是一个换行符,与RS
等效的是ORS
,
特殊变量
最后的字段$NF
:
awk -F : '{print $NF}' /etc/passwd
NR
当前行号:
# 默认
docker images | awk '{print $2,$3}'
TAG IMAGE
1.0 98c79a7bb07f
# 输出行号大于1的行
docker images | awk 'NR > 1 {print $2,$3}'
1.0 98c79a7bb07f
条件判断
条件语句以 if
开头,计算圆括号中的表达式
# 语法
if ( expression )
action1
[else
action2]
如果条件表达式 expression
的值为真(非零或),执行 action1
,当存在 else
语句时,如果值为假(零或空)则执行 action2
。
一个条件表达式可能包含算数运算符,关系运算符,或布尔运算符 。
测试变量是否非零值,如果是非零值则打印X
if ( x ) print x
测试是否相等
if ( x == y ) print z
测试是否匹配
if ( x ~ /[yY](es)?/ ) print x
如果有多个语句组成的,需要用一对大括号将这些语句括起来,如果使用分号后面的换行符则是可选的。
if ( expression ) {
action1;
action2;
}
循环语句
循环是一种用于重复执行一个或多个操作的结构,awk中可以使用 while,do
,或者 for
语句。
while循环
while ( condition ) {
action
}
条件表达式在循环的顶部进行计算,如果为真则执行循环体action部分,相反则不执行循环体;
i = 1
while ( i <= 4 ) {
print $i
++i
}
do 循环
do {
action
while ( condition )
示例
BEGIN {
do {
++x
print x
} while ( x <= 4 )
}
注意,do
与 while
循环之间的区别,do
循环至少执行一次循环体。
for 循环
for ( set_counter; test_counter; increment_counter ) {
action
}
for 循环由3个表达式组成
set_counter 计数器初始值
test_counter 测试条件
increatment_counter 递增计数器
递加
for ( i=1; i<=NF; i++) {
print $i
}
递减
for ( i=NF; i>=1; i--) {
print $i
}
其它影响流控的语句
- break 语句,退出循环。
- continue 语句,跳过当前循环进入下一次循环。
- Next 语句,读入下一个输入行。
- Next语句,的典型应用是结合系统变量
FILENAME
(当前文件名)连续从文件读取内容,忽略其它的操作,直到文件全部读完。 - exit语句,将退出主输入循环并且跳到
END
部分;如果没有定义END
部分,或者END中
使用exit
语句,则终止脚本执行。
exit语句,也可以作为表达式的返回状态,如果没有提供表达式,那么返回0,如果为exit
设置一个值,则awk 退出状态为此值。
echo "" | awk '{exit}' ; echo $?
0
echo "" | awk '{ exit 1}' ; echo $? #注意exit 1
1