awk详解
awk是用来做什么的?
AWK是一种处理文本文件的语言,是一个强大的文本分析工具,常用于格式化输出文件或者报告
awk常见用法?
- 用法一
shell awk '{[pattern] action}' {filenames} # 行匹配语句 awk '' 只能用单引号
以上命令会默认以TAB符对行进行分割处理,分割后,通过$1、$2、$3 ...对每一行的子项进行匹配,$0为整行。 以下为几个实用的例子:shell # 提取nginx中用户的IP地址 awk '{print $1}' access.log # awk的输入文件可以通过管道符"|"实现, tail -n 20 access.log|awk '{print $1}' # 这样就可以只提取access.log文件中最后20条的IP地址了
- 用法二
用法一中的方法只能针对TAB分隔符的文件进行处理,实际使用过程中,分隔符如果是","、":"、"|"、"::"等等, 那么,此时需要引入一个参数-F
:shell awk -F #-F相当于内置变量FS, 指定分割字符
在使用-F
时,如果分隔符是单个的字符,则直接在-F
后面追加则可,有无空格都可以,如果是字符串时,需要使用单引号进行区别如-F '::'
:shell # /etc/passwd 是一个很好的实验对象,它是以':'号做为分隔符 awk -F: '{print $1}' /etc/passwd
- 用法三
当你需要在输出的结果中,显示一些自定义的变量时,通过-v
参数则可以轻松的实现:shell awk -v[变量名]=[变量值] # 设置变量,-v后面可以不留空格
设置的变量可以直接参与运算:shell awk -v num=1 '{print $1+num}' test.log
- 用法四
当我们处理文件内容时,可能需要使用一些条件进行区别与过虑:shell awk '[条件] {处理方式}' {文件名}
如,当我们需要查看nginx日志中,所有访问状态为500的访问链接:shell awk '$9=500 {print $7}' test.log
当然还是以像其它编程语言一样,使用多个条件,awk支持各自逻辑与或非shell awk '$1="127.0.0.1" && $9=500 {print $7}' test.log
#### 使用拓展 * 使用正则表达式 ```
awk '/REG/{action}' filename
/REG/为正则表达式,可以将$0中,满足条件记录 送入到:action进行处理.
正则表达式的语法与其它语言类似,区别较大的是`~`与`!~`的使用,它们分别用于表示匹配与不匹配.
上面的使用模式省略了默认的一个参数,完整的写法可以理解成:
awk '$0 ~ /REG/{action}' filename
因此,照这个用法我们可以很简单的找出ningx日志中,IP包含198的的行:
awk '$1 ~ /198/{print $1}' test.log
反过来,也很容易的找出IP中不包含198的行:
awk '$1 !~ /198/{print $1}' test.log
* awk脚本基本结构
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
`BEGIN`语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
`END`语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
`pattern`语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供`pattern`语句块,则默认执行`{print}`,即打印每一个读取到的行,awk读取的每一行都会执行该语句块。
实现一个将nginx的行索引与IP地址打印,并以TAB制表符分隔的例子:
awk 'BEGIN{i=0;print "Index\tIP"}{printf "%d\t%s\n",i,$1;i++}END{printf "Total\t%d\n",i+1}' test.log
* 使用外部脚本文件
当处理的脚本内容过长时,可以单独写成一个脚本文件进行处理,通过`-f`参数进行脚本文件调用:
$ cat cal.awk #!/bin/awk -f #运行前 BEGIN { math = 0 english = 0 computer = 0
printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
} #运行中 { math+=$3 english+=$4 computer+=$5 printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5 } #运行后 END { printf "---------------------------------------------\n" printf " TOTAL:%10d %8d %8d \n", math, english, computer printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR } $ awk -f cal.awk log.txt
#### 内置预设
* awk内置变量(预定义变量)
说明:[A][N][P][G]表示第一个支持变量的工具,[A]=awk、[N]=nawk、[P]=POSIXawk、[G]=gawk
```shell
$n 当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。
$0 这个变量包含执行过程中当前行的文本内容。
[A] FILENAME 当前输入文件的名。
[A] FS 字段分隔符(默认是任何空格)。
[A] NF 表示字段数,在执行过程中对应于当前的字段数。
[A] NR 表示记录数,在执行过程中对应于当前的行号。
[A] OFMT 数字的输出格式(默认值是%.6g)。
[A] OFS 输出字段分隔符(默认值是一个空格)。
[A] ORS 输出记录分隔符(默认值是一个换行符)。
[A] RS 记录分隔符(默认是一个换行符)。
[N] ARGC 命令行参数的数目。
[G] ARGIND 命令行中当前文件的位置(从0开始算)。
[G] IGNORECASE 如果为真,则进行忽略大小写的匹配。
[G] FIELDWIDTHS 字段宽度列表(用空格键分隔)。
[G] CONVFMT 数字转换格式(默认值为%.6g)。
[N] ARGV 包含命令行参数的数组。
[N] ERRNO 最后一个系统错误的描述。
[N] RSTART 由match函数所匹配的字符串的第一个位置。
[N] RLENGTH 由match函数所匹配的字符串的长度。
[N] SUBSEP 数组下标分隔符(默认值是34)。
[P] ENVIRON 环境变量关联数组。
[P] FNR 同NR,但相对于当前文件。
内置函数
length
获得字符长度awk '/robots/{print length($0)}' host.access.log
substr
截取字符串awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}'
system
调用外部应用程序awk 'BEGIN{b=system("ls -al");print b;}'
getline
逐行读取外部文件awk 'BEGIN{while(getline < "/etc/passwd"){print $0;};close("/etc/passwd");}'
sprintf
格式化字符串输出%d 十进制有符号整数 %u 十进制无符号整数 %f 浮点数 %s 字符串 %c 单个字符 %p 指针的值 %e 指数形式的浮点数 %x %X 无符号以十六进制表示的整数 %o 无符号以八进制表示的整数 %g 自动选择合适的表示法
总结
总的来说,
awk
在linux
平台下处理各种日志文件应该是再合适不过的了.awk
是琢行处理, 因此即便日志文件过大也能轻松应对.
排序
用sort
进行排序
-t sep 定义列与列之间的分割符号,默认位空格/tab
-k sum 定义第一个域,如 sort -k1 则表示对第一列进行排序
-n 数字排序
-r 倒序,不传默认正排序
-rn 参数组合,意为按数字进行倒序。
如何对IIS日志文件中的状态码进行排序:
awk '{a[$9]+=1}END {for(i in a){print a[i]" "i}}' u_ex181109.log|sort -k1 -rn
# 截取URL中部分做为键值
awk '{a[substr($5,0,40)]+=1}END {for(i in a){print a[i]" "i}}' u_ex181111.log|sort -k1 -rn|less
# 截取URL中包含`registerphone`
awk '$5 ~/registerphone/ {print $5,$6,$7}' u_ex181110.log
# 对URL中隐含registerphone的IP地址进行排序
awk '$5 ~/registerphone/ {a[$6]+=1}END{for(i in a){print a[i]" "i}}' u_ex181110.log
唯一行
NAME
uniq -- 报告或过滤文件中的重复行
SYNOPSIS
uniq [-c | -d | -u] [-i] [-f num] [-s chars] [input_file [output_file]]
DESCRIPTION
The uniq utility reads the specified input_file comparing adjacent lines, and writes a copy of each unique input line to the output_file. If input_file is a single dash (`-') or absent, the standard input is read. If output_file is
absent, standard output is used for output. The second and succeeding copies of identical adjacent input lines are not written. Repeated lines in the input will not be detected if they are not adjacent, so it may be necessary to sort the
files first.
-c 或 --count 在每列旁边显示该行重复出现的次数。
-d 或 --repeated 仅显示重复出现的行列。
-f <栏位> 或 --skip-fields=<栏位> 忽略比较指定的栏位。
-s <字符位置> 或 --skip-chars=<字符位置> 忽略比较指定的字符。
-u 或--unique 仅显示出一次的行列。
-w <字符位置> 或 --check-chars=<字符位置> 指定要比较的字符。
--help 显示帮助。
--version 显示版本信息。
联合sort与uniq命令使用:
$ cat testfile1 # 原有内容
test 30
Hello 95
Linux 85
test 30
Hello 95
Linux 85
test 30
Hello 95
Linux 85
这时我们就可以使用 sort:
$ sort testfile1 | uniq
Hello 95
Linux 85
test 30
统计各行在文件中出现的次数:
$ sort testfile1 | uniq -c
3 Hello 95
3 Linux 85
3 test 30
在文件中找出重复的行:
$ sort testfile1 | uniq -d
Hello 95
Linux 85
test 30
取首或取尾
sort -n tmp.log | uniq -c|head -10 # 取排序前10条
sort -n tmp.log | uniq -c|tail -10 # 取排序最后10条
格式化输出
xargs
一般是和管道一起使用。
命令格式:
somecommand |xargs -item command
-a file 从文件中读入作为sdtin
-e flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。
-p 当每次执行一个argument的时候询问一次用户。
-n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。
-t 表示先打印命令,然后再执行。
-i 或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替。
-r no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了。
-s num 命令行的最大字符数,指的是 xargs 后面那个命令的最大命令行字符数。
-L num 从标准输入一次读取 num 行送给 command 命令。
-l 同 -L。
-d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符。
-x exit的意思,主要是配合-s使用。。
-P 修改最大的进程数,默认是1,为0时候为as many as it can ,这个例子我没有想到,应该平时都用不到的吧。
统计IP地址访问:
awk '{print $6}' u_ex181213.log |uniq -c|sort -n
列表对齐
|column -t
常用命令
统计 非 200 状态的 nginx 日志,并按时间倒序排序
awk '$0~/10\/Nov\/2020/ {print $0}' /usr/local/nginx/logs/access.log | awk '$9!=200 {print $0}'|sort -k4 -r
ip访问次数排序
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c| sort -k1 -n