http://www.hcs.harvard.edu/~dholland/computers/awk.html
http://www.vectorsite.net/tsawk_1.html
http://stud.wsi.edu.pl/~robert/awk/
http://gregable.com/2010/09/why-you-should-know-just-little-awk.html
1、打印列
假设数据如下:
07.46.199.184 [28/Sep/2010:04:08:20] "GET /robots.txt HTTP/1.1" 200 0 "msnbot" 123.125.71.19 [28/Sep/2010:04:20:11] "GET / HTTP/1.1" 304 - "Baiduspider"
打印整行,$0是整行:
$ cat ./test.txt | awk '{print $0}' 07.46.199.184 [28/Sep/2010:04:08:20] "GET /robots.txt HTTP/1.1" 200 0 "msnbot" 123.125.71.19 [28/Sep/2010:04:20:11] "GET / HTTP/1.1" 304 - "Baiduspider"
其中{和}之间的是命令,一般来说,对输入的每一行执行一遍。
对于$x,默认用任何空白符号做分割。
$x表示第x列,例如:
$ echo 'this is a test' | awk '{print $3}' a
$ cat ./test.txt | awk '{print $1}' 07.46.199.184 123.125.71.19
2、列数变量NF
有的时候,你并不知道数据会有多少列,可以用NF打印列数量:
$ echo 'this is a test' | awk '{print NF}' 4
照葫芦画瓢,可以使用$NF表示最后一列:
$ echo 'this is a test' | awk '{print $NF}' test
甚至可以使用组合的NF:$(NF-1),表示倒数第2列:
$ echo 'this is a test' | awk '{print $1, $(NF-1)}' this a
3、行数变量NR
与NF对应,NR表示行数,例如,可以给前面的log文件的前面,加上行号:
$ cat ./test.txt | awk '{print "<"NR"> "$0}' <1> 07.46.199.184 [28/Sep/2010:04:08:20] "GET /robots.txt HTTP/1.1" 200 0 "msnbot" <2> 123.125.71.19 [28/Sep/2010:04:20:11] "GET / HTTP/1.1" 304 - "Baiduspider"
如上:注意在print之内的其他变量要用引号""。
4、指定其他(非空白)分隔符
还是对于如下数据,我们想提取出日期,例如“28/Sep/2010”:
07.46.199.184 [28/Sep/2010:04:08:20] "GET /robots.txt HTTP/1.1" 200 0 "msnbot" 123.125.71.19 [28/Sep/2010:04:20:11] "GET / HTTP/1.1" 304 - "Baiduspider"
我们来做个拆解动作,首先提取“[28/Sep/2010:04:08:20]”,然后再提取“[28/Sep/2010”:
其中-F为指定分隔符。
$ cat ./test.txt | awk '{print $2}' | awk -F : '{print $1}' [28/Sep/2010 [28/Sep/2010
我们也可以把分隔符写在awk命令内。
$ cat ./test.txt | awk '{print $2}' | awk 'BEGIN{FS=":"}{print $1}' [28/Sep/2010 [28/Sep/2010
到了这一步,我们发现还剩下最前面一个[没有被去掉,可以sed搞定。
$ cat ./test.txt | awk '{print $2}' | awk 'BEGIN{FS=":"}{print $1}' | sed 's/^\[//' 28/Sep/2010 28/Sep/2010
5、数值计算
加法:
$ echo 5 4 | awk '{print $1 + $2}' 9
除法:
$ echo 5 4 | awk '{print $1 / $2}' 1.25
最诡异的,默认乘法:
$ echo 5 4 | awk '{print $1 $2}' 54
6、多行语句
在一个语句块{}中,awk也能写多语句、循环,语句之间用分号;分开。变量直接指定。
对每一行处理:求该行所有列的均值:
$ cat ./num.txt 1 2 3 4 5 6 liheyuan@coder4-pc:~$ cat ./num.txt | awk '{sum=0;for(i=1;i<NF;i++) sum+=$i;print sum/NF}' 0.5 1.5 2.5
7、多个块
上面的例子很傻,一般来说,我们是要求所有数值的均值
我们可以使用END,END之后的语句块{}表示当所有Input执行完毕后,再执行它。
例如,求所有输入数值的均值:
$ cat ./num.txt 1 3 5 liheyuan@coder4-pc:~$ cat ./num.txt | awk '{sum+=$1} END {print sum/NR}' 3
Block可以有条件的执行,当$1==0时,执行:
awk ' $1==0 { print $2 }'
8、加入If逻辑
还是上述日志分析,如果我们只想打印HTTP Response状态为200的,怎么办呢?
$ cat ./test.txt | awk '{ if ( $(NF-2) == 200 ) {print $0} }' 07.46.199.184 [28/Sep/2010:04:08:20] "GET /robots.txt HTTP/1.1" 200 0 "msnbot"
9、循环逻辑
假设如下的文件:
$ cat ./num.txt 1 2 3 4 5
累加,并逐行输出:
$ cat ./num.txt | awk '{a+=$1 ; print a}' 1 3 6 10 15
累加,最后只输出一行:
$ cat ./num.txt | awk '{a+=$1}END{print a}' 15
10、printf
awk支持类似C语言的printf格式。
跳过第1列,打印剩余的列,同时打印行号,列号:
$ cat ./num.txt | awk '{for(i=1;i<=NF;i++) printf "<%d,%d> %s \n", NR, i, $i ;}' <1,1> 1 <2,1> 3 <3,1> 5
11、随机输出行 ( 2013.11.29更新 )
awk中内置了rand()函数,我们可以结合sort、head的方法,随机抽样N行。
seq 1 100 | awk 'BEGIN {srand()} {print rand()"\t"$1}' | sort -k1,1n | head -n 10 | awk '{print $2}'
这个组合脚本分为如下几个部分:
- seq 1 100会生成1~100的整数,每个各一行,就是Python中的range()啦。
- 第一组awk,BEGIN中的srand()初始化随机种子,rand()会在已有的1列文本前,多输出一列随机值
- 使用sort,对数据进行排序,按照数值,只第1列(随机数那列)。
- 抽样取 top 10。
- 只输出第2列,将第1列,多生成的随机数去掉。
12、在awk中使用Shell变量 ( 2013.12.02更新 )
参考了stackoverflow:Can we use shell variables in awk?
在awk中,是无法像$foo这样引用shell脚本中的变量的。
我们需要通过-v设定,使用时也不需要$,如下:
echo | awk -v my_var=4 '{print "My var is " my_var}' My var is 4
13、使用awk的循环,直接输出数据
awk的常见用法是:针对每一行输入,进行处理,输出。
但也可以无输入文件,直接利用awk的循环语法。例如构建100万条的测试数据。
BEGIN{ for(i=0;i<100000000;i++){ uid = rand()*1000000; print i"\x01topic_"i"\x01"uid; } }
写在BEGIN里面的,表示在处理Input之前执行的预处理操作。只会执行一次。
14、关联数组用法
awk中的关联数组,类似于C++中的Map。
下面的例子,在BEGIN中设定了关联数组a,并打印所有的KV。在行处理阶段,若行在a中存在key,则打印k v。
BEGIN{ a["1"] = "a" a["2"] = "b" print "--IN BEGIN--\n" for(key in a) { print key, a[key] } print "--END BEGIN--\n" } { if(a[$1]) { print $1, a[$1] } }
更多的例子,可以看看这个《AWK Arrays Explained with 5 Practical Examples》
清空关联数组:
split("", doc)
15、处理多个文件
如果在awk中处理多个文件,是需要一些Trick的。
标准模板为:
awk 'FNR==NR{a[$1]=$2 FS $3;next}{ print $0, a[$1]}' file1 file2
其中" awk 'FNR==NR{a[$1]=$2 FS $3;next} " 这句,是在处理第1个文件(file1)。
FNR是当前文件处理的行数,NR是所有文件处理的行数。所以当FNR==NR时,就是第1个文件。
当读取第2个文件(file2)后,FNR重置为1,而NR继续增长,所以此时会执行{ print $0, a[$1]}这个。
16、awk中读取其他文件
在awk中,除了stdin和awk命令传入的文件外,我们也可以读入额外的文件。
下面的例子中,在BEGIN阶段,读入了文件"file_name"。
awk 'BEGIN{ while(( getline < "file_name" ) > 0 ) { print $0 } }
在上述的while循环中,可以和正常的awk语法一样,按照分割副,自动处理文件。
17、awk中split的用法
split($0,a,":" ); print a[1]; for(i in a) { print i, a[i]; }
18、各种分割符的用途
- FS : Input field separator variable.
- OFS : Output Field Separator Variable
- RS : Record Separator variable (默认一行作为1个record)
- ORS : Output Record Separator Variable
- NR : Number of Records Variable
- NF: Number of Fields in a record
- FILENAME : Name of the current input file
- FNR : Number of Records relative to the current input file
19、累加相同key的value值并输出
cat ./data.txt | awk -F ':' '{ seen[$1] += $2 } END { for (i in seen) print i, seen[i] }' | sort
TO BE CONTINUE