awk快速教程

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}'

这个组合脚本分为如下几个部分:

  1. seq 1 100会生成1~100的整数,每个各一行,就是Python中的range()啦。
  2. 第一组awk,BEGIN中的srand()初始化随机种子,rand()会在已有的1列文本前,多输出一列随机值
  3. 使用sort,对数据进行排序,按照数值,只第1列(随机数那列)。
  4. 抽样取 top 10。
  5. 只输出第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、各种分割符的用途

  1. FS : Input field separator variable.
  2. OFS : Output Field Separator Variable
  3. RS : Record Separator variable (默认一行作为1个record)
  4. ORS : Output Record Separator Variable
  5. NR : Number of Records Variable
  6. NF: Number of Fields in a record
  7. FILENAME : Name of the current input file
  8. 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

 

Leave a Reply

Your email address will not be published. Required fields are marked *