基础语法说明
awk 是按分割符进行行处理的工具
1. 命令参数说明
$ awk
Usage: awk [POSIX or GNU style options] -f progfile [--] file ...
Usage: awk [POSIX or GNU style options] [--] 'program' file ...
POSIX options: GNU long options: (standard)
-f progfile --file=progfile 从脚本文件中读取awk命令
-F fs --field-separator=fs 指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F ‘|’
-v var=val --assign=var=val 赋值一个用户定义变量
Short options: GNU long options: (extensions)
-b --characters-as-bytes
-c --traditional
-C --copyright
-d[file] --dump-variables[=file]
-D[file] --debug[=file]
-e 'program-text' --source='program-text'
-E file --exec=file
-g --gen-pot
-h --help
-i includefile --include=includefile 加载导入文件
-l library --load=library 加载 awk 库
-L[fatal|invalid] --lint[=fatal|invalid] 打印不能向传统unix平台移植的结构的警告
-M --bignum 大数模式
-N --use-lc-numeric 使用指定数字解析方式
-n --non-decimal-data 无数字数据
-o[file] --pretty-print[=file] 输出到文件
-O --optimize 使用优化
-p[file] --profile[=file]
-P --posix 是否 posix 模式
-r --re-interval
-s --no-optimize 无优化
-S --sandbox 沙盒模式
-t --lint-old 传统 unix 警告
-V --version 版本信息
To report bugs, see node `Bugs' in `gawk.info'
which is section `Reporting Problems and Bugs' in the
printed version. This same information may be found at
https://www.gnu.org/software/gawk/manual/html_node/Bugs.html.
PLEASE do NOT try to report bugs by posting in comp.lang.awk.
gawk is a pattern scanning and processing language.
By default it reads standard input and writes standard output.
Examples:
gawk '{ sum += $1 }; END { print sum }' file
gawk -F: '{ print $1 }' /etc/passwd
基本命令格式 awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file
即可
2. 内置环境说明
- 常见环境变量
变量 | 描述 |
---|---|
$n | 当前记录的第n个字段,字段间由FS分隔。 |
$0 | 完整的输入记录。 |
ARGC | 命令行参数的数目。 |
ARGIND | 命令行中当前文件的位置(从0开始算)。 |
ARGV | 包含命令行参数的数组。 |
CONVFMT | 数字转换格式(默认值为%.6g) |
ENVIRON | 环境变量关联数组。 |
ERRNO | 最后一个系统错误的描述。 |
FIELDWIDTHS | 字段宽度列表(用空格键分隔)。 |
FILENAME | 当前文件名。 |
FNR | 同NR,但相对于当前文件。 |
FS | 字段分隔符(默认是任何空格)。 |
IGNORECASE | 如果为真,则进行忽略大小写的匹配。 |
NF | 当前记录中的字段数。 |
NR | 当前记录数。 |
OFMT | 数字的输出格式(默认值是%.6g)。 |
OFS | 输出字段分隔符(默认值是一个空格)。 |
ORS | 输出记录分隔符(默认值是一个换行符)。 |
RLENGTH | 由match函数所匹配的字符串的长度。 |
RS | 记录分隔符(默认是一个换行符)。 |
RSTART | 由match函数所匹配的字符串的第一个位置。 |
SUBSEP | 数组下标分隔符(默认值是\034)。 |
- 常见运算符
运算符 | 描述 |
---|---|
= += -= *= /= %= ^= **= | 赋值 |
?: | C条件表达式 |
|| | 逻辑或 |
&& | 逻辑与 |
~ ~! | 匹配正则表达式和不匹配正则表达式 |
< <= > >= != == | 关系运算符 |
空格 | 连接 |
+ - | 加,减 |
* / & | 乘,除与求余 |
+ - ! | 一元加,减和逻辑非 |
^ *** | 求幂 |
++ -- | 增加或减少,作为前缀或后缀 |
$ | 字段引用 |
in | 数组成员 |
- 内部函数
内容较多,参加 awk 命令使用入门基础详解
3. 执行模式
通常使用 pattern { action }
的模式进行执行,比如 echo "AWK lines"| awk '/^AWK/ {print "这行以 AWK 开头"}'
4. 脚本编写方式
脚本与命令类似,可将多个匹配条件综合到一个执行过程里
$ cat nginx_499.awk
#!/bin/awk -f
# 标记执行过程开始
BEGIN {
printf "BEGIN : %s \n", "start stat log...."
}
# 仅第十项参数为 200 的行执行相应 action
$10 == 200 {
ca++
cb++
# 使用 next 表示后续的模式不被执行
next
}
# 仅第十项参数为 499 的行执行相应 action
$10 == 499 {
if ($16 > 4.99) {
cc++
} else if ($16 <= 4.99) {
cd++
}
}
# 这里无 pattern 情况下,所有行都会被执行到
{
# 打印当前行所有内容
print $0
ca++
ce++
}
# 标记执行过程结束
END {
printf "END : %s \n", "stat result: "
printf "total request count: %d \n", ca
printf "normal response count: %d \n", cb
printf "failed response count: %d \n", ce
printf "status code: 499, response time > 4.99s count: %d \n", cc
printf "status code: 499, response time <= 4.99s count: %d \n", cd
}
然后执行 awk -f nginx_499.awk nginx.log
即可得到如下结果
$ awk -f nginx_499.awk nginx.log
BEGIN : start stat log....
...省略错误...
END : stat result:
total request count: 7957358
normal response count: 7897586
failed response count: 59772
status code: 499, response time > 4.99s count: 1029
status code: 499, response time <= 4.99s count: 36803
注
通过 chmod +x nginx_499.awk
即可使用 nginx_499.awk nginx.log
直接处理日志
5. 多行分析
内容相对比较容易解析,如下:
$ cat status.log
web01[192.168.2.100]
httpd ok
tomcat ok
sendmail ok
web02[192.168.2.101]
httpd ok
postfix ok
web03[192.168.2.102]
mysqld ok
httpd ok
则简单的使用变量即可实现转换,如下:
$ cat multi-line.awk
#!/bin/awk -f
# 取出当前标签
/^web/ {
T = $0
# 标签行处理之后,跳至下一行
next
}
# 所有非标签行进行标签化打印即可
{
printf "%s: \t %s \n", T, $0
}
执行 awk -f multi-line.awk status.log
即可,或者直接单行 awk '/^web/{T=$0;next;}{print T ":\t" $0;}' status.log
$ awk -f multi-line.awk status.log
web01[192.168.2.100]: httpd ok
web01[192.168.2.100]: tomcat ok
web01[192.168.2.100]: sendmail ok
web02[192.168.2.101]: httpd ok
web02[192.168.2.101]: postfix ok
web03[192.168.2.102]: mysqld ok
web03[192.168.2.102]: httpd ok
6. 常见语法
- 变量定义
echo "hoge fuga" | \
awk '
# 定义变量
BEGIN {
var = "hoge"
}
# 使用 match 函数匹配第一个字符
match($1, "^" var) {
print "match line: " var " content: " $0
}
'
或
echo "hoge fuga" | \
awk '
BEGIN {
var="hoge"
}
# 使用 match 函数匹配第一个字符
$1 ~ /^var/ {
print "match line: " var " content: " $0
}'
- 指定行处理
NR == 10 { print "当前为第10行, 内容是: " $0 }
- 多条件
NR >= 10 || $NF ~ /[0-9]+/ { print "第十行之后,或者最后一个字段数据为数字的行"}
- 常见处理
# 含有 `特殊处理` 行进行特殊处理
/特殊处理/ {
# 判定共有 5 个字段
if(NF == 5){
# 有 5 个字段的行执行如下打印
print "Number of fields: 5"
}
# 执行for循环操作
for(i=0; i < 10; i++){
# 执行十次如下打印
printf("这是第 %02d: %2d 次循环操作。\n", i, i+1)
}
# 执行 while 循环操作
while(1){
# 死循环
print "循环开始。"
print "循环结束。"
# 使用 break 跳出死循环。
break
}
# 使用 next 结束处理,当前块之后的所有块都不会被执行。
next
}
常见分析场景
1. 日志超时统计
在做 nginx 日志分析时,常会需要统计响应时长大于 某时间 请求数量统计,如下是日志:
[26/Feb/2020:14:34:52 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.170:8082 3.540 3.515 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:52 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.174:8082 2.987 2.966 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:52 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.170:8082 2.986 2.965 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:52 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.172:8082 5.983 5.959 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:52 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.172:8082 2.967 2.948 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:52 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.175:8082 3.006 2.985 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:52 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.170:8082 3.005 2.981 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:52 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 200 131 "-" 200 xxx.xxx.xxx.170:8082 0.499 0.453 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:34:59 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 200 132 "-" 200 xxx.xxx.xxx.175:8082 0.544 0.499 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:07 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 200 131 "-" 200 xxx.xxx.xxx.174:8082 0.499 0.463 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.41.228 - xxx.xxx.cn:10080 POST "POST /topics/dc_ginfo HTTP/1.1" 499 0 "-" - xxx.xxx.xxx.174:8082 2.990 2.967 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.172:8082 2.983 2.957 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.104.156 - xxx.xxx.cn:10080 POST "POST /topics/dc_ginfo HTTP/1.1" 499 0 "-" - xxx.xxx.xxx.170:8082 3.003 2.981 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.174:8082 2.981 2.957 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.175:8082 2.989 2.965 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.36.240 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.175:8082 2.992 2.972 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.171:8082 2.968 2.950 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 499 25 "-" - xxx.xxx.xxx.170:8082 2.994 2.976 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:20 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 200 130 "-" 200 xxx.xxx.xxx.174:8082 2.499 2.453 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:28 +0800] xxx.xxx.104.156 - xxx.xxx.cn:10080 POST "POST /topics/dc_ginfo HTTP/1.1" 200 106 "-" 200 xxx.xxx.xxx.171:8082 1.539 1.499 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
[26/Feb/2020:14:35:28 +0800] xxx.xxx.34.132 - xxx.xxx.cn:10080 POST "POST /topics/dcmessage HTTP/1.1" 200 132 "-" 200 xxx.xxx.xxx.171:8082 0.499 0.460 "Mozilla/5.0 (Windows NT 5.2; rv:13.0) Gecko/20100101 Firefox/13.0.1" "-"
对状态码为 499 且响应时间 5s 的分界点数量统计:
$ awk '{if ($10 == 499 && $16 > 4.99) {ca++} else if ($10 == 499 && $16 <= 4.99) {cb++}} END{print "status 499 and resp time > 4.99: "ca
" \nstatus 499 and resp time <= 4.99: "cb}' nginx.log
status 499 and resp time > 4.99: 1
status 499 and resp time <= 4.99: 14