1. 介绍

Awk是由Aho、Weinberger和Kernighan三位科学家开发的,特别擅长文本处理的linux 工具,该工具是 linux 下最常用的工具之一。Awk不是普通的工具,其实,也是一种编程语言,在编程语言排行榜上也能排上号的。

Google 的一位工程师(他的博客最近访问不了)写了一篇文章,介绍Awk,标题为《Why you should learn just a
little awk》,该文章简单的介绍了Awk 的用法,在Reddit 和 Ycombinator
上引起了热烈讨论。好玩的是,文章的最后作者还留下邮箱,大意是"如果你是那种对awk感兴趣的人,毫无疑问,你就是那种我希望可以在Google共事的
人,如果你感兴趣,可以给我简历(ggrothau@gmail.com),我可以确保它一定会出现在合适的招聘者面前,而不是遗落在我们每天收到的那一
大堆简历中"。

我一直关注的一个博客,博主去AT&T实验室实习,总结的文章里有提到,AT&T里的科学家和工程师每天都在Awk/sed/grep
和管道中折腾。如果现在要让我选择最喜欢的编程语言的话,我会选择Awk ,Awk
不但简单易学,功能强大,而且能够通过管道与Shell脚本很好的交互,这是其他编程语言所不能及的。当然了,我们要用合适的工具和合适的语言做合适的事
情,Awk并不是万能的,它的强项就是文本处理以及与Shell 脚本的交互。

我猜想我喜欢Awk 的另一个原因是它的语法与C 语言很像,相对于Python
和其他语言,Awk更符合我的思维方式。Awk和C语言很像,但是省去了初始化,内存分配,严格的语法等烦人的细节。这里还不得不提的是,Awk
三位创始人之一 Kernighan 就是《The C Programming Language》的第一作者,它的语法与C
语言相像也就不足为奇了吧。

2. Awk
2.1 Awk 程序的结构

下面根据我的理解简单介绍下Awk 的语法,然后摘录一些小例子演示Awk 的用法,再通过几个程序,演示Awk 的强大之处,最后,给大家推荐本参考书吧。

Awk 程序的结构如下:

awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }  file

Awk程序由3部分组成:BEGIN语句块,END语句块,和能够使用模式(正则表达式/关系表达式)匹配的通用语句,这三部分中的任何一部分都可以省 略,BEGIN语句块在程序最开始执行,通常完成一些初始化的工作,END语句块在程序的最后执行,通常在最后格式化输出结果。模式部分的工作原理如下: 首先获取一行,检查该行是否与提供的样式匹配,如果匹配就执行与该样式对应的{}中的语句。

简单来说,就是首先执行BEGIN 语句块(如果有的话),然后逐行检查file 中的数据是否满足pattern, 如果满足,就执行{}中的语句,如果不满足,就什么也不做,在程序的最后执行END语句块(如果有的话)。

也可以通过管道获取数据,如下所示:

cat file | awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }  

2.2 Hello,world

下面我们来看一下在Awk 中如何打印Hello,world , 在上面已经提到过,Awk 程序由三部分组成(BEGIN语句块,pattern , END语句块),三部分中的任何一部分都可以省略,最简单的方法打印Hello,world 如下:

只有BEGIN 语句块

echo | awk 'BEGIN{ print "Hello, world"}'

只有END 语句块

echo | awk 'END{ print "Hello, world"}'

只有pattern 部分

echo | awk '{ print "Hello, world"}' 

在pattern 部分,因为pattern 为空,只有{}语句块,所以,程序认为每一行数据满足模式,都执行{}语句块中的内容。

2.3 深入理解三个语句块

再次强调:Awk 在程序执行之前执行BEGIN
语句块,然后读入一行数据,检查该行数据是否满足模式,如果满足,则执行该模式对应的{}语句块里的内容,再次读入下一行数据,进行第二次模式检查,直到
读完所有的数据,在程序的最后,执行END语句块中的语句。一个标准的Awk 程序如下:

seq 5 | awk 'BEGIN{ print "begin"} $1 > 2 { print } END{ print "end" }'

输出结果:

begin
3
4
5
end

该程序首先执行BEGIN语句块,输出begin,然后检查模式$1 > 2(稍后解释)是否满足,满足就执行{}块中的语句,即print ,在Awk 中,print 后面没有任何东西,意思就是打印该行内容,在程序最后执行END语句块,打印end 。

2.4 Awk中的特殊变量

Awk 之所以处理文本非常方便,是因为它提供了很多内置的功能和变量,让我们可以方便的操作各数据。

NR: 表示记录的数量(number of record),在执行过程中,相当于当前行号
    NF: 表示当前行的字段数量(number of fields)
    $0 这个标量包含执行过程中当前行的文本内容
    $1 这个标量包含当前行中第一个字段的内容
    $2 当前行第二个字段的内容
    FS 域分割符,相当于sort 命令和cut 命令中的-d选项
    OFS 输出时,域分割符,默认是"\t"

2.5 Examples

下面来看几个简单的例子,假设你有一个文件emp.data,里面的数据如下:

Beth    4.00    0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

其中,第一列是员工的名字,第二列是每小时的工钱,第3列是工作时长,为了得到每个员工的工钱,只需要下面这条语句即可:

awk '$3 > 0 { print $1, $2 * $3 }' emp.data

由于Awk 已经将数据帮我们分割好了,我们只需要直接引用就好了,当我们使用变量$1 的时候,就指当前行的第一个域,也就是姓名,我们使用变量$3的时候,就是引用第3个域,即工作时长。

你如果想看哪些员工没有工作,即工作时长为0的员工,只需要像下面这样即可:

awk '$3 == 0 { print }' emp.data

或者这样:

awk '$3 == 0 { print }' emp.data

上面两条语句用于演示,print 不加任何参数,就是打印当前行中所有的内容,$0 也是代表当前行的所有内容,所以,上面两条语句的作用是一样的,如果,你只想看工作时长为0的员工的名字,则:

awk '$3 == 0 { print $1 }' emp.data

你还可以把行号打印出来,以方便一眼看出有几个员工没有工作:

awk '$3 == 0 { print NR, ":\t",  $1 }' emp.data

如果,你只想看Kathy 的工资,则:

awk '$1 == Kathy { print $1, $2 * $3}' emp.data

或者使用正则表达式:

awk '$1 ~ /Kathy/ { print $1, $2 * $3}' emp.data

Awk 中也可以用printf 格式化输出,printf 的使用方法和C 语言中printf 的使用方法一样,如:

awk '$3 > 0{ printf("Total pay for %s is %.2f\n", $1, $2 * $3) }' emp.data

模式是可以结合使用的,如:

awk '$2 >= 4 || $3 >= 20 {print $0}' emp.data

输出结果如下:

Beth    4.00    0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

2.6 数据验证

模式一般是用来选择需要处理的数据的,例如,上面我们就用$3 > 0 来选择工作时长不为0的员工,还可以向下面这样:

awk 'NR < 5 {} '         # 只处理前4行,即行号小于5的行
awk 'NR == 1, NR == 4{}' # 行号在1到4之间
awk '/linux/{}' # 包含样式linux 的行
awk '!/lunux/{}' # 不包含样式linux 的行

除此之外,模式还可以用来验证数据,如下所示:

NF != 3   { print $0, "number of fields is not equal to 3" }
$2 < 3.35 { print $0, "rate is below minimum wage" }
$2 > 10 { print $0, "rate exceeds $10 per hour" }
$3 < 0 { print $0, "negative hours worked" }
$3 > 60 { print $0, "too many hours worked" }

2.7 BEGIN语句块的作用

BEGIN语句块通常用于输出头信息,或者预先处理其他信息。例如,我们可以这样打印标题:

awk 'BEGIN{print "NAME    RATE    HOURS"; print ""} { print $0 }' emp.data

则输出信息如下:

NAME   RATE HOURS

Beth    4.00    0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

更常用的用法是在BEGIN语句块中执行FS,即域分隔符,前面已经说过,域分隔符类似与sort命令和cut 命令中的-d选项,例如,你要在查看每个用于和他的Home目录,使用cut 的话,方法如下:

cut -d: -f1,6 /etc/passwd

使用Awk也很方便

awk 'BEGIN{ FS=":" }{ print $1, $6}' /etc/passwd

我们也可以将awk写入文件之中

#print.awk - print user and it's home dir
BEGIN{FS = ":"}
{
print $1, $6
}

执行的时候只需要输入下如命令即可:

awk -f print.awk /etc/passwd

或者

cat /etc/passwd | awk -f print.awk

2.8 END语句块

END语句块主要用来输出一些汇总信息,例如,我们想知道到底有多少员工:

awk 'END { print NR, "employees"}' emp.data

我们也可以轻易得到总的工资和每个员工的平均工资:

#count.awk - compute the average pay
{
pay = pay + $2 * $3
}
END{ print NR, employees
print "total pay is", pay
print "average pay is", pay/NR
} awk -f count.awk emp.data

输出结果如下:

6 employees
total pay is 337.5
average pay is 56.25

2.9 Awk中的控制语句

Awk 中的控制语句和C用法和C语言一样,不过没有Switch。

#reverse- print input in reverse order by line
{ line[NR] = $0 } #remember each input line
END{ i = NR
while ( i > 0 ){
print line[i]
i--
}
} #reverse - print input in reverse order by line
{ line[NR] = $0 } #remember each input line
END{
for ( i = NR; i > 0; i--)
print line[i]
}

除此之外,for 循环略有差别, Awk 中的for 循环有两种方式:

for ( i = 0; i < 10; i++) {print $i;}

for ( i in array){ print array[$i]; }

2.10 关联数组

所谓关联数组,就是既可以用数字也可以用字符串做下标的数组。为了演示关联数组的用法,考虑下面几个实例。假设有文件file,里面的数据如下:

Item1,200
Item2,500
Item3,900
Item2,800
Item1,600

在file 文件中,有些Item 出现了多次,假设要对相同的Item 求和:

awk -F, '{ a[$1] += $2} END{ for ( i in a ) print i "," a[i]}' file

输出结果如下:

Item1, 800
Item2, 1300
Item3, 900

这里出现了两个新知识点,一个使用了关联数组,我们不需要初始化数组,也不用知道数组中有多少元素,因为我们可以在用第二种for循环的形式来输出数组中 的内容;第二个知识点是print 语句,print 语句中没有使用逗号分隔,而是直接将各字段连接起来,在Awk中连接字符串的方法如下:

str = "hello" "world" "!" $2

如果我们只想打印每项中数字最大的那一项,而不是把它们加起来,则可以像下面这样:

awk -F, '{if (a[$1] < $2) a[$1] = $2} END{ for( i in a ){ print i, a[i]}}'
OFS=, file

输出结果如下:

Item1,600
Item2,800
Item3,900

统计每项出现的次数:

awk -F, '{ a[$1]++}END{ for ( i in a) print i, a[i]}' file

输出结果如下:

Item1 2
Item2 2
Item3 1

打印每一项的第一次出现:

awk -F, '!a[$1]++' file

输出结果如下:

Item1,200
Item2,500
Item3,900

在这里,只有模式,没有{}语句块,则默认原样输出。

2.11 Awk 中的内建函数

Awk 提供了很多函数来帮助用户提高工作效率

3. A handful of Userful "one-liners" print the total number of input lines

END { print NR }

print the tenth input line:

NR == 10

Print the last field of every input line

{ print $NF}

print the last field of the last input line

{field = $NF}
END { print field}

print every input line with more than four fields

NF > 4

print every input line in which the last field is more than 4

$NF > 4

print the total number of fields in all input lines:

{ nf = nf + NF }
END { print nf }

print the total number of lines that contain Beth

/Beth/{nlines += 1}
END { print nlines}

print the largest first field and the line that contains it (assumes some $1 is positive)

$1 > max { max = $1; maxline = $0 }
END { print max, maxline }

print every line that has at least one field

NF > 0

print every line longer than 80 characters

length($0) > 80

print the number of fields in every line followed by the line itself:

{print NF, $0}

print the first two fields in opposite order, of every line

{ print $2, $1}

Exchange the first two fields of every ine and then print the line

{ temp = $1; $1 = $2; $2 = temp; print}

print every line with the first field replaced by the line number

{$1 = NR; print}

print every line after erasing the second filed

{$2 = ""; print}

print in reverse order the fields of every line

{ for ( i = 1; i <= NF; ++i)printf ("%s ", $i)
    print ""
}

print the sums of the fields of every line

{
   sum = 0
   for ( i = 1; i <= NF; ++i) sum += $i
   print sum
}

Add up all fields in all lines and print the sum

{ for ( i = 1; i <= NF; ++i ) sum += $i
END { print sum}

print every line after replacing each field by its absolute value

{
    for ( i = 1; i <= NF; ++i) if ( $i < 0 ) $i = -$i
    print
}

4. Awk 程序的应用

4.1 统计单词出现次数

我们分析的第一个程序是一个统计单词出现次数的程序,由于Awk 提供关联数组,而且默认将变量初始化为0 ,所以Awk解决这个问题可谓是得心应手,我在之前的文章中讨论过这个问题,在那篇文章中试图用c,c++,shell 来解决。

但是这个问题用Awk 来解决最简单,还能轻易的处理标点符号的问题,程序如下:

#wordfreq- print number of occurences of each word
#input: text
#output: number-word pairs sorted by number { gsub(/[.=,:;!?(){}]/, "") #remove punctuation
for ( i = 1; i <= NF; i++)
count[$i]++
}
END {
for ( w in count)
print count[w], w | "sort -rn"
}

4.2 数据处理

虽然Awk 不是全能的,但是也能解决很多问题,但是,它最擅长的还是数据处理,假设有如下数据:

USSR    8649    275 Asia
Canada 3852 25 North America
China 3705 1032 Asia
USA 3615 237 North America
Brazil 3286 134 South America
India 1267 746 Asia
Mexico 762 78 North America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe

第一列是国家名,第二列是该国所占的面积,第三列是人口数量,最后一列是所属的洲,如果,我们希望得按洲的名称升序排序,再按每个国家人口密集度降序排序的表格,我们应该怎么做呢?

我不知道Excel 怎么做,应该是可以,学习Awk 的好处就在于,你可以通过Awk 解决很多Excel 解决的问题,而不用去研究傻瓜式的Excel , 很明显,Awk 更灵活。

下面来解决这个问题,Awk 解决这类问题的基本思路是,准备数据--排序(或其他处理)--格式化输出。

4.3 Markov 链算法

Markov 链算法的定义见这篇文章,我在那篇文章中讨论了各种语言实现Markov 链算法的实现,这简直就是演示Awk 优点的问题,具体程序如下,不再作详细讨论。

# Copyright (C) 1999 Lucent Technologies
# Excerpted from 'The Practice of Programming'
# by Brian W. Kernighan and Rob Pike # markov.awk: markov chain algorithm for 2-word prefixes
BEGIN { MAXGEN = 10000; NONWORD = "\n"; w1 = w2 = NONWORD } { for (i = 1; i <= NF; i++) { # read all words
statetab[w1,w2,++nsuffix[w1,w2]] = $i
w1 = w2
w2 = $i
}
} END {
statetab[w1,w2,++nsuffix[w1,w2]] = NONWORD # add tail
w1 = w2 = NONWORD
for (i = 0; i < MAXGEN; i++) { # generate
r = int(rand()*nsuffix[w1,w2]) + 1 # nsuffix >= 1
p = statetab[w1,w2,r]
if (p == NONWORD)
exit
print p
w1 = w2 # advance chain
w2 = p
}
}

学习资料
最好的学习资料非《The Awk Programming Language》不可,这本书是Awk 的创始人,也《The C
Programming
Language》的作者,老头子文笔非常之好,非常简单的语言,把问题解释的非常清楚,要让我选诺贝尔文学奖获得者,我一定会第一给他投票,老头子实在
是太NB了,他的其他书《The Practice of Programming》和《Unix 编程环境》也很不错,强烈推荐。

转自:http://www.lvtao.net/tool/awk.html

【转】awk用法介绍的更多相关文章

  1. awk用法介绍

    Awk 程序的结构如下: awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" } ...

  2. awk 用法(使用入门)

    转自:http://www.cnblogs.com/emanlee/p/3327576.html awk 用法:awk ' pattern {action} '  变量名    含义 ARGC   命 ...

  3. sed和awk用法

    sed和awk用法 Sed sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行替换.删除.新增.选取等特定工作,下面先了解一下sed的用法sed命令行格式为 ...

  4. 好压(HaoZip)的命令行模式用法介绍

    好压压缩软件,又叫“2345好压”,是一款国产的优秀压缩软件,目前是免费的,据官网介绍,该软件永久免费.官网地址:http://haozip.2345.com/ 本文主要对该软件的命令行模式用法进行介 ...

  5. sql事务(Transaction)用法介绍及回滚实例

    sql事务(Transaction)用法介绍及回滚实例 事务(Transaction)是并发控制的单位,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务, S ...

  6. STL vector用法介绍

    STL vector用法介绍 介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和f ...

  7. 怎么通过activity里面的一个按钮跳转到另一个fragment(android FragmentTransaction.replace的用法介绍)

    即:android FragmentTransaction.replace的用法介绍 Fragment的生命周期和它的宿主Activity密切相关,几乎和宿主Activity的生命周期一致,他们之间最 ...

  8. awk用法小结(作者总结)

    http://www.chinaunix.net/old_jh/24/691456.html http://wenku.baidu.com/view/ebac4fc658f5f61fb736664d. ...

  9. Oracle CASE WHEN 用法介绍[Z]

    Oracle CASE WHEN 用法介绍 1. CASE WHEN 表达式有两种形式 --简单Case函数 CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ...

随机推荐

  1. TextView 的新特性,Autosizing 到底是如何实现的? | 源码分析

    一.前言 Hi,大家好,我是承香墨影! 前两天聊了一下 Autosizing 的使用,反映还不错.毕竟是这种能解决实际问题的新 Api,确实在需要的时候,用起来会很顺手. 简单回顾一下,Autosiz ...

  2. flask配置管理

    配置管理 复杂的项目需要配置各种配置环境,如果设置项缺少可以直接硬编码进来,比如下面的方式: app.config['DEBUG']=True app.config['SECRET_KEY'] = ' ...

  3. feed 流数据请求时机的两个思路

    最近 SF 首页 进行了大改版,效果如下: 其他地方都没什么难点,中间的 feed 流思考了不少时间,效果需要类似微博或者知乎 feed 流.之前一直没有做过类似的功能,现总结两个方案. 方案一 方案 ...

  4. gitignore样例解析

    # 这是注释行 -- 被忽略 *.a # 忽略所有以 .a 为扩展名的文件 !lib.a # 但是lib.a 文件或目录不要忽略,即使前面设置了对*.a的忽略 /TODO # 只忽略此目录下的TODO ...

  5. Redux 介绍

    本文主要是对 Redux 官方文档 的梳理以及自身对 Redux 的理解. 单页面应用的痛点 对于复杂的单页面应用,状态(state)管理非常重要.state 可能包括:服务端的响应数据.本地对响应数 ...

  6. AtCoder Grand Contest 013

    这场打得蛮菜的,很晚才出BC,还一堆罚时…… A - Sorted Arrays 题目大意:将给定数列划分成单调不增或单调不减的区间,求最少区间数. 贪心即可. #include<cstdio& ...

  7. Uva 10339 - Watching Watches【数论,暴力】

    题目链接:10339 - Watching Watches 题意:两个时钟,一个每天慢a秒,一个每天慢b秒,问两钟重新相遇的时刻 1圈有12 * 60 * 60秒,然后1圈 / abs(a - b), ...

  8. [ZOJ3256] Tour in the Castle

    插头DP+矩阵乘法 m喜闻乐见地达到了10^9级别..而n<=7,并且没有障碍..所以列与列之间的转移时一样的..就可以上矩乘了. 感觉自己快没救了..看半天题解还是不懂.. http://ww ...

  9. [bzoj2288][POJ Challenge]生日礼物

    用堆维护双向链表来贪心... 数据范围显然不容许O(nm)的傻逼dp>_<..而且dp光是状态就n*m个了..显然没法优化 大概就会想到贪心乱搞了吧...一开始想贪心地通过几段小的负数把正 ...

  10. vue -- v-cloak解决刷新或者加载出现闪烁(显示变量)

    在使用vue绑定数据的时候,渲染页面时会出现变量闪烁,例如 <div class="#app"> <p>{{value.name}}</p> & ...