awk的简单用法1
Awk的简单用法
下面没有讲述a w k的全部特性,也不涉及a w k的深层次编程,仅讲述使用a w k执行行操作及怎样从文本文件和字符串中抽取信息。
QUOTE:
内容有:
" 抽取域。
" 匹配正则表达式。
" 比较域。
" 向a
w k传递参数。
" 基本的a
w k行操作和脚本。
a w k语言的最基本功能是在文件或字符串中基于指定规则浏览和抽取信息。a w k抽取信息后,才能进行其他文本操作。完整的a w k脚本通常用来格式化文本文件中的信息。
1 调用awk
有三种方式调用a w k,第一种是命令行方式,如:
CODE:
awk [-F
fild-separator] 'commands' input-file(s)
这里,c o m m a n d s是真正的a w k命令。
上面例子中, [ - F域分隔符]是可选的,因为a w k使用空格作为缺省的域分隔符,因此如果要浏览域间有空格的文本,不必指定这个选项,但如果要浏览诸如p a s s w d文件,此文件各域以冒号作为分隔符,则必须指明- F选项,如:
CODE:
awk
-F: 'commands' input-file(s)
第二种方法是将所有a w k命令插入一个文件,并使a w k程序可执行,然后用a w k命令解释器作为脚本的首行,以便通过键入脚本名称来调用它。
第三种方式是将所有的a w k命令插入一个单独文件,然后调用:
CODE:
awk -f
awk-script-file input-files(s)
- f选项指明在文件a
w k _ s c r i p t _ f i l e中的a w k脚本, i n p u t _ f i l e ( s )是使用a w k进行浏览的文件名。
2 awk脚本
在命令中调用a w k时,a w k脚本由各种操作和模式组成。
如果设置了- F选项,则a w k每次读一条记录或一行,并使用指定的分隔符分隔指定域,但如果未设置- F选项,a
w k假定空格为域分隔符,并保持这个设置直到发现一新行。当新行出现时,a w k命令获悉已读完整条记录,然后在下一个记录启动读命令,这个读进程将持续到文件尾或文件不再存在。
参照表,a w k每次在文件中读一行,找到域分隔符(这里是符号#),设置其为域n,直至一新行(这里是缺省记录分隔符),然后,划分这一行作为一条记录,接着a w k再次启动下一行读进程。
awk读文件记录的方式
QUOTE:
域1
分隔符
域2 分隔符
域3
分隔符 域4及换行
P. B u n n y (记录1
) # 0 2 / 9 9
# 4 8
# Yellow \n
J . Tr o l l (记录2
) # 0 7 / 9 9
# 4 8 4 2 #
Brown-3 \n
2.1 模式和动作
任何a w k语句都由模式和动作组成。在一个a w k脚本中可能有许多语句。模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。
模式可以是任何条件语句或复合语句或正则表达式。模式包括两个特殊字段B E G I N和E
N D。使用B
E G I N语句设置计数和打印头。B
E G I N语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行。E N D语句用来在a w k完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明模式, a w k总是匹配或打印行数。
实际动作在大括号{ }内指明。动作大多数用来打印,但是还有些更长的代码诸如i
f和循环(l
o o p i n g)语句及循环退出结构。如果不指明采取动作, a w k将打印出所有浏览出来的记录。
2. 域和记录
a w k执行时,其浏览域标记为$
1,$
2 . . . $ n。这种方法称为域标识。使用这些域标识将更容易对域进行进一步处理。
使用$ 1 , $ 3表示参照第1和第3域,注意这里用逗号做域分隔。如果希望打印一个有5个域的记录的所有域,不必指明$ 1 , $ 2 , $
3 , $ 4 , $ 5,可使用$
0,意即所有域。Aw
k浏览时,到达一新行,即假定到达包含域的记录末尾,然后执行新记录下一行的读动作,并重新设置域分隔。
注意执行时不要混淆符号$和s h e l l提示符$,它们是不同的。
为打印一个域或所有域,使用p r i n t命令。这是一个a w k动作(动作语法用圆括号括起来)。
1. 抽取域
真正执行前看几个例子,现有一文本文件g
r a d e . t x t,记录了一个称为柔道数据库的行信息。
CODE:
$ cat grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26
此文本文件有7个域,即(1)名字、(2)升段日期、(3)学生序号、(4)腰带级别、(5)年龄、(6)目前比赛积分、(7)比赛最高分。
因为域间使用空格作为域分隔符,故不必用-
F选项划分域,现浏览文件并导出一些数据。在例子中为了利于显示,将空格加宽使各域看得更清晰。
2. 保存a
w k输出
有两种方式保存s h e l l提示符下a w k脚本的输出。最简单的方式是使用输出重定向符号>文件名,下面的例子重定向输出到文件w
o w。
CODE:
$ awk '{print $0}'
grade.txt >wow
$ cat grade.txt
使用这种方法要注意,显示屏上不会显示输出结果。因为它直接输出到文件。只有在保证输出结果正确时才会使用这种方法。它也会重写硬盘上同名数据。
第二种方法是使用t e e命令,在输出到文件的同时输出到屏幕。在测试输出结果正确与否时多使用这种方法。例如输出重定向到文件d e l e t e _ m e _ a n d _ d i e,同时输出到屏幕。使用这种方法,在a
w k命令结尾写入|
tee delete_me_and_die。
CODE:
$ awk '{print $0}'
grade.txt | tee delete_me_and_die
3. 使用标准输入
在深入讲解这一章之前,先对a w k脚本的输入方法简要介绍一下。实际上任何脚本都是从标准输入中接受输入的。为运行本章脚本,使用a w k脚本输入文件格式,例如:
QUOTE:
belts.awk
grade_student.txt
也可替代使用下述格式:
使用重定向方法:
belts.awk < grade2.txt
或管道方法:
grade2.txt | belts.awk
4. 打印所有记录
CODE:
$ awk '{print $0}'
grade.txt
a w k读每一条记录。因为没有模式部分,只有动作部分{print $0}(打印所有记录),这个动作必须用花括号括起来。上述命令打印整个文件。
5. 打印单独记录
假定只打印学生名字和腰带级别,通过查看域所在列,可知为f
i e l d - 1和f
i e l d - 4,因此可以使用$
1和$
4,但不要忘了加逗号以分隔域。
CODE:
$ awk '{print $1,$4}'
grade.txt
M.Tans Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown
6. 打印报告头
上述命令输出在名字和腰带级别之间用一些空格使之更容易划分,也可以在域间使用t a b键加以划分。为加入t a b键,使用t a b键速记引用符\ t,后面将对速记引用加以详细讨论。也可以为输出文本加入信息头。本例中加入n a m e和b e l t及下划线。下划线使用\ n,强迫启动新行,并在\ n下一行启动打印文本操作。打印信息头放置在B E G I N模式部分,因为打印信息头被界定为一个动作,必须用大括号括起来。在a w k查看第一条记录前,信息头被打印。
CODE:
$ awk 'BEGIN {print
"Name Belt\n-----------------------------------"}{print
$1"\t",$4}' grade.txt
Name Belt
-----------------------------------
M.Tans Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown
7. 打印信息尾
如果在末行加入end of report信息,可使用E N D语句。E N D语句在所有文本处理动作执行完之后才被执行。E
N D语句在脚本中的位置放置在主要动作之后。下面简单打印头信息并告之查询动作完成。
CODE:
$ awk 'BEGIN {print
"Name\n--------"}{print $1} END {print "end-of-report"}'
grade.txt
Name
--------
M.Tans
J.Lulu
P.Bunny
J.Troll
L.Tansl
8. awk错误信息提示
几乎可以肯定,在使用a w k时,将会在命令中碰到一些错误。a w k将试图打印错误行,但由于大部分命令都只在一行,因此帮助不大。
系统给出的显示错误信息提示可读性不好。使用上述例子,如果丢了一个双引号, a w k将返回:
CODE:
$ awk 'BEGIN {print
"Name\n--------}{print $1} END {"end-of-report"}' grade.txt
awk: cmd. line:1: BEGIN {print "Name\n--------}{print $1} END
{"end-of-report"}
awk: cmd. line:1:
^ unterminated string
当第一次使用a w k时,可能被错误信息搅得不知所措,但通过长时间和不断的学习,可总结出以下规则。在碰到a w k错误时,可相应查找:
QUOTE:
" 确保整个a w k命令用单引号括起来。
" 确保命令内所有引号成对出现。
" 确保用花括号括起动作语句,用圆括号括起条件语句。
" 可能忘记使用花括号,也许你认为没有必要,但a w k不这样认为,将按之解释语法
。
如果查询文件不存在,将得到下述错误信息:
CODE:
$ awk 'END {print
NR}' grades.txt
awk: cmd. line:2: fatal: cannot open file `grades.txt' for reading (没有那个文件或目录)
9.awk 键盘输入
如果在命令行并没有输入文件g r a d e . t
x t,将会怎样?
CODE:
$ awk 'BEGIN {print
"Name\n--------"}{print $1} END {"end-of-report"}'
Name
--------
B E G I N部分打印了文件头,但a
w k最终停止操作并等待,并没有返回s h e l l提示符。这是因为a
w k期望获得键盘输入。因为没有给出输入文件, a w k假定下面将会给出。如果愿意,顺序输入相关文本,并在输入完成后敲<Ct r l - D >键。如果敲入了正确的域分隔符, a w k会像第一个例子一样正常处理文本。这种处理并不常用,因为它大多应用于大量的打印稿。
2.3awk中正则表达式及其操作
在g r e p一章中,有许多例子用到正则表达式,这里将不使用同样的例子,但可以使用条件操作讲述a w k中正则表达式的用法。
这里正则表达式用斜线括起来。例如,在文本文件中查询字符串G
r e e n,使用/
G r e e n /可以查出单词G
r e e n的出现情况。
2.4元字符
这里是a w k中正则表达式匹配操作中经常用到的字符,详细情况请参阅本书第7章正则表达式概述。
CODE:
\ ^ $ . [] | () * + ?
这里有两个字符第7章没有讲到,因为它们只适用于a w k而不适用于g r e p或s e d。它们是:
QUOTE:
+ 使用+匹配一个或多个字符。
? 匹配模式出现频率。例如使用/X Y?Z/匹配X Y Z或Y Z。
条件操作符
a w k条件操作符
操作符描述操作符描述
< 小于>
= 大于等于
< = 小于等于~
匹配正则表达式
= = 等于!~
不匹配正则表达式
!= 不等于
1. 匹配
为使一域号匹配正则表达式,使用符号‘~’后紧跟正则表达式,也可以用i f语句。a w k中i f后面的条件用()括起来。
观察文件g r a d e . t x t,如果只要显示b r o w n腰带级别可知其所在域为f i e l d - 4,这样可以写出表达式{if($4~/brown/)
print }意即如果f
i e l d - 4包含b
r o w n,打印它。如果条件满足,则打印匹配记录行。可以编写下面脚本,因为这是一个动作,必须用花括号{ }括起来。
CODE:
[root@Linux_chenwy
sam]# awk '{if($4~/Brown/) print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26
匹配记录找到时,如果不特别声明,
a w k缺省打印整条记录。使用i
f语句开始有点难,但不要着急,因为有许多方法可以跳过它,并仍保持同样结果。下面例子意即如果记录包含模式b r o w n,就打印它:
CODE:
[root@Linux_chenwy
sam]# awk '$0 ~ /Brown/' grade.txt
J.Troll 07/99 4842 Brown-3 12 26
2. 精确匹配
假定要使字符串精确匹配,比如说查看学生序号4
8,文件中有许多学生序号包含4 8,如果在f
i e l d - 3中查询序号4
8,a
w k将返回所有序号带4
8的记录:
CODE:
[root@Linux_chenwy
sam]# awk '{if($3~/48/) print$0}' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
为精确匹配4 8,使用等号= =,并用单引号括起条件。例如$ 3
CODE:
[root@Linux_chenwy
sam]# awk '$3=="48" {print$0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
[root@Linux_chenwy sam]# awk '{if($3=="48") print$0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
3. 不匹配
有时要浏览信息并抽取不匹配操作的记录,与~相反的符号是!~,意即不匹配。像原来使用查询b r o w n腰带级别的匹配操作一样,现在看看不匹配情况。表达式$0
!~/brown/,意即查询不包含模式b
r o w n腰带级别的记录并打印它。
注意,缺省情况下, a w k将打印所有匹配记录,因此这里不必加入动作部分。
CODE:
[root@Linux_chenwy
sam]# awk '$0 !~ /Brown/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
可以只对f i e l d - 4进行不匹配操作,方法如下:
CODE:
[root@Linux_chenwy
sam]# awk '{if($4~/Brown/) print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26
如果只使用命令awk$4
!="brown"{print $0} grade.txt,将返回错误结果,因为用引号括起了b r o w n,将只匹配‘b r o w n而不匹配b r o w n - 2和b r o w n - 3,当然,如果想要查询非b r o w n - 2的腰带级别,可做如下操作:
CODE:
[root@Linux_chenwy
sam]# awk '$4!="Brown-2" {print $0}' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
4. 小于
看看哪些学生可以获得升段机会。测试这一点即判断目前级别分f
i e l d - 6是否小于最高分f
i e l d - 7,在输出结果中,加入这一改动很容易。
CODE:
[root@Linux_chenwy
sam]# awk '{if($6 < $7) print $0}' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
5. 小于等于
对比小于,小于等于只在操作符上做些小改动,满足此条件的记录也包括上面例子中的输出情况。
CODE:
[root@Linux_chenwy
sam]# awk '{if($6 <= $7) print $1}' grade.txt
M.Tans
J.Lulu
J.Troll
6. 大于
CODE:
[root@Linux_chenwy
sam]# awk '{if($6 > $7) print $1}' grade.txt
P.Bunny
L.Tansl
7. 设置大小写
为查询大小写信息,可使用[ ]符号。在测试正则表达式时提到可匹配[
]内任意字符或单词,因此若查询文件中级别为g r e e n的所有记录,不论其大小写,表达式应为‘ / [ G g ] r e e n /’
CODE:
[root@Linux_chenwy
sam]# awk '/[Gg]reen/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
8. 任意字符
抽取名字,其记录第一域的第四个字符是a,使用句点.。表达式/ ^ . . . a /意为行首前三个字符任意,第四个是a,尖角符号代表行首。
CODE:
[root@Linux_chenwy
sam]# awk '$1 ~ /^...a/' grade.txt
M.Tans 5/99 48311 Green 8 40
9. 或关系匹配
为抽取级别为y e l l o w或b r o w n的记录,使用竖线符|。意为匹配| 两边模式之一。注意,使用竖线符时,语句必须用圆括号括起来。
CODE:
[root@Linux_chenwy
sam]# awk '$0 ~/(Yellow|Brown)/' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26
上面例子输出所有级别为Ye l l o w或B r o w n的记录。
使用这种方法在查询级别为G r e e n或g r e e n时,可以得到与使用[ ]表达式相同的结果。
CODE:
[root@Linux_chenwy
sam]# awk '/^M/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
10. 行首
不必总是使用域号。如果查询文本文件行首包含M的代码,可简单使用下面^符号:
CODE:
[root@Linux_chenwy
sam]# awk '/^M/' grade.txt
复合表达式即为模式间通过使用下述各表达式互相结合起来的表达式:
QUOTE:
&& AND : 语句两边必须同时匹配为真。
|| O R:语句两边同时或其中一边匹配为真。
! 非求逆
11. AND
打印记录,使其名字为‘ P. B u n n
y且级别为Ye
l l o w,使用表达式(
$ 1 = = " P. B u n n y " & &$ 4 = = " Ye l l o w "
),意为&
&两边匹配均为真。完整命令如下:
CODE:
[root@Linux_chenwy
sam]# awk '{if ($1=="P.Bunny" && $4=="Yellow")
print $0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
12. Or
如果查询级别为Ye l l o w或B r o w n,使用或命令。意为“ | |”符号两边的匹配模式之一或全部为真。
CODE:
[root@Linux_chenwy
sam]# awk '{if ($4=="Yellow" || $4~/Brown/) print $0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26
原来不一定得加print,下面我自己对例一二做了一下
CODE:
1
[root@Linux_chenwy sam]# awk '$4~/Brown/' grade.txt
J.Troll 07/99 4842 Brown-3 12 26
CODE:
2
[root@Linux_chenwy sam]# awk '$3=="48"' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
CODE:
[root@Linux_chenwy
sam]# awk '$3="48"' grade.txt
M.Tans 5/99 48 Green 8 40 44
J.Lulu 06/99 48 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 48 Brown-3 12 26
2中,我把=和==写错了,呵呵,一个是赋值,一个是等于