shell基础九:awk |
终于看经常用的到的awk了
下面没有讲述a w k的全部特性,也不涉及a w k的深层次编程,仅讲述使用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,记录了一个称为柔道数据库的行信息。
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
L.Tansl 05/99 4712 Brown-2 12 30 28
因为域间使用空格作为域分隔符,故不必用- F选项划分域,现浏览文件并导出一些数据。在例子中为了利于显示,将空格加宽使各域看得更清晰。
2. 保存a w k输出
有两种方式保存s h e l l提示符下a w k脚本的输出。最简单的方式是使用输出重定向符号>文件名,下面的例子重定向输出到文件w o w。
$ 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。
在深入讲解这一章之前,先对a w k脚本的输入方法简要介绍一下。实际上任何脚本都是从标准输入中接受输入的。为运行本章脚本,使用a w k脚本输入文件格式,例如:
也可替代使用下述格式:
使用重定向方法:
belts.awk < grade2.txt
或管道方法:
grade2.txt | belts.awk
4. 打印所有记录
5. 打印单独记录
假定只打印学生名字和腰带级别,通过查看域所在列,可知为f i e l d - 1和f i e l d - 4,因此可以使用$ 1和$ 4,但不要忘了加逗号以分隔域。
M.Tans Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown-3
L.Tansl Brown-2
上述命令输出在名字和腰带级别之间用一些空格使之更容易划分,也可以在域间使用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查看第一条记录前,信息头被打印。
Name Belt
-----------------------------------
M.Tans Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown-3
L.Tansl Brown-2
如果在末行加入end of report信息,可使用E N D语句。E N D语句在所有文本处理动作执行完之后才被执行。E N D语句在脚本中的位置放置在主要动作之后。下面简单打印头信息并告之查询动作完成。
Name
--------
M.Tans
J.Lulu
P.Bunny
J.Troll
L.Tansl
几乎可以肯定,在使用a w k时,将会在命令中碰到一些错误。a w k将试图打印错误行,但由于大部分命令都只在一行,因此帮助不大。
系统给出的显示错误信息提示可读性不好。使用上述例子,如果丢了一个双引号, a w k将返回:
awk: cmd. line:1: BEGIN {print "Name/n--------}{print $1} END {"end-of-report"}
awk: cmd. line:1: ^ unterminated string
" 确保命令内所有引号成对出现。
" 确保用花括号括起动作语句,用圆括号括起条件语句。
" 可能忘记使用花括号,也许你认为没有必要,但a w k不这样认为,将按之解释语法
如果查询文件不存在,将得到下述错误信息:
awk: cmd. line:2: fatal: cannot open file `grades.txt' for reading (没有那个文件或目录)
如果在命令行并没有输入文件g r a d e . t x t,将会怎样?
Name
--------
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章正则表达式概述。
? 匹配模式出现频率。例如使用/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,打印它。如果条件满足,则打印匹配记录行。可以编写下面脚本,因为这是一个动作,必须用花括号{ }括起来。
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
假定要使字符串精确匹配,比如说查看学生序号4 8,文件中有许多学生序号包含4 8,如果在f i e l d - 3中查询序号4 8,a w k将返回所有序号带4 8的记录:
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
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
有时要浏览信息并抽取不匹配操作的记录,与~相反的符号是!~,意即不匹配。像原来使用查询b r o w n腰带级别的匹配操作一样,现在看看不匹配情况。表达式$0 !~/brown/,意即查询不包含模式b r o w n腰带级别的记录并打印它。
注意,缺省情况下, a w k将打印所有匹配记录,因此这里不必加入动作部分。
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
L.Tansl 05/99 4712 Brown-2 12 30 28
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
看看哪些学生可以获得升段机会。测试这一点即判断目前级别分f i e l d - 6是否小于最高分f i e l d - 7,在输出结果中,加入这一改动很容易。
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
对比小于,小于等于只在操作符上做些小改动,满足此条件的记录也包括上面例子中的输出情况。
M.Tans
J.Lulu
J.Troll
P.Bunny
L.Tansl
为查询大小写信息,可使用[ ]符号。在测试正则表达式时提到可匹配[ ]内任意字符或单词,因此若查询文件中级别为g r e e n的所有记录,不论其大小写,表达式应为‘ / [ G g ] r e e n /’
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
抽取名字,其记录第一域的第四个字符是a,使用句点.。表达式/ ^ . . . a /意为行首前三个字符任意,第四个是a,尖角符号代表行首。
M.Tans 5/99 48311 Green 8 40 44
L.Tansl 05/99 4712 Brown-2 12 30 28
为抽取级别为y e l l o w或b r o w n的记录,使用竖线符|。意为匹配| 两边模式之一。注意,使用竖线符时,语句必须用圆括号括起来。
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
使用这种方法在查询级别为G r e e n或g r e e n时,可以得到与使用[ ]表达式相同的结果。
M.Tans 5/99 48311 Green 8 40 44
不必总是使用域号。如果查询文本文件行首包含M的代码,可简单使用下面^符号:
|| O R:语句两边同时或其中一边匹配为真。
! 非求逆
打印记录,使其名字为‘ P. B u n n y且级别为Ye l l o w,使用表达式( $ 1 = = " P. B u n n y " & &$ 4 = = " Ye l l o w " ),意为& &两边匹配均为真。完整命令如下:
P.Bunny 02/99 48 Yellow 12 35 28
如果查询级别为Ye l l o w或B r o w n,使用或命令。意为“ | |”符号两边的匹配模式之一或全部为真。
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
[root@Linux_chenwy sam]# awk '$4~/Brown/' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
[root@Linux_chenwy sam]# awk '$3=="48"' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
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 26
L.Tansl 05/99 48 Brown-2 12 30 28
awk内置变量
a w k有许多内置变量用来设置环境信息。这些变量可以被改变。表9 - 3显示了最常使用的一些变量,并给出其基本含义。
A R G C 命令行参数个数
A R G V 命令行参数排列
E N V I R O N 支持队列中系统环境变量的使用
FILENAME a w k浏览的文件名
F N R 浏览文件的记录数
F S 设置输入域分隔符,等价于命令行- F选项
N F 浏览记录的域个数
N R 已读的记录数
O F S 输出域分隔符
O R S 输出记录分隔符
R S 控制记录分隔符
E N V I R O N 支持系统设置的环境变量,要访问单独变量,使用实际变量名,例如E N V I R O N [“E D I TO R”] =“Vi”。
F I L E N A M E支持a w k脚本实际操作的输入文件。因为a w k可以同时处理许多文件,因此如果访问了这个变量,将告之系统目前正在浏览的实际文件。
F N R支持a w k目前操作的记录数。其变量值小于等于N R。如果脚本正在访问许多文件,每一新输入文件都将重新设置此变量。
F S用来在a w k中设置域分隔符,与命令行中- F选项功能相同。缺省情况下为空格。如果用逗号来作域分隔符,设置F S = ","。
N F支持记录域个数,在记录被读之后再设置。
O F S允许指定输出域分隔符,缺省为空格。如果想设置为#,写入O F S = " # "。
O R S为输出记录分隔符,缺省为新行( / n)。
R S是记录分隔符,缺省为新行( / n )。
要快速查看记录个数,应使用N R。比如说导出一个数据库文件后,如果想快速浏览记录个数,以便对比于其初始状态,查出导出过程中出现的错误。使用N R将打印输入文件的记录个数。print NR放在E N D语法中。
5
[root@chenwy sam]# awk '{print NF,NR,$0} END{print FILENAME}' grade.txt
7 2 J.Lulu 06/99 48317 green 9 24 26
7 3 P.Bunny 02/99 48 Yellow 12 35 28
7 4 J.Troll 07/99 4842 Brown-3 12 26 26
7 5 L.Tansl 05/99 4712 Brown-2 12 30 28
grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
sam
rc.sybase
/usr/sam
[root@chenwy sam]# echo "/usr/local/etc/rc.sybase" | awk '{print $NF}'
/usr/local/etc/rc.sybase
awk操作符
在a w k中使用操作符,基本表达式可以划分为数字型、字符串型、变量型、域及数组元素,前面已经讲过一些。下面列出其完整列表。
在表达式中可以使用下述任何一种操作符。
? 条件表达操作符
|| && ! 并、与、非(上一节已讲到)
~!~ 匹配操作符,包括匹配和不匹配
< <= == != >> 关系操作符
+ - * / % ^ 算术操作符
+ + -- 前缀和后缀
1. 设置输入域到域变量名
在a w k中,设置有意义的域名是一种好习惯,在进行模式匹配或关系操作时更容易理解。
一般的变量名设置方式为n a m e = $ n,这里n a m e为调用的域变量名, n为实际域号。例如设置学生域名为n a m e,级别域名为b e l t,操作为n a m e = $ 1 ; b e l t s = $ 4。注意分号的使用,它分隔a w k命令。下面例子中,重新赋值学生名域为n a m e,级别域为b e l t s。查询级别为Ye l l o w的记录,并最终打印名称和级别。
P.Bunny is belt Yellow
有两种方式测试一数值域是否小于另一数值域。
1) 在B E G I N中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在B E G I N部分赋值是很有益的,可以在a w k表达式进行改动时减少很多麻烦。
使用关系操作必须用圆括号括起来。
下面的例子查询所有比赛中得分在2 7点以下的学生。
用引号将数字引用起来是可选的,“2 7”、2 7产生同样的结果。
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
J.Lulu 06/99 48317 green 9 24 26
J.Troll 07/99 4842 Brown-3 12 26 26
当在a w k中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的a w k复本。a w k会在变量N R或N F变量中反映出修改痕迹。
为修改数值域,简单的给域标识重赋新值,如: $ 1 = $ 1 + 5,会将域1数值加5,但要确保赋值域其子集为数值型。
修改M . Ta n s l e y的目前级别分域,使其数值从4 0减为3 9,使用赋值语句$ 6 = $ 6 - 1,当然在实施修改前首先要匹配域名。
M.Tans 39 44
J.Lulu 24 26
P.Bunny 35 28
J.Troll 26 26
L.Tansl 30 28
M.Tans 39 44
修改文本域即对其重新赋值。需要做的就是赋给一个新的字符串。在J . Tr o l l中加入字母,使其成为J . L . Tr o l l,表达式为$ 1 = " J . L . Tr o l l ",记住字符串要使用双秒号( " "),并用圆括号括起整个语法。
M.Tans
J.Lulu
P.Bunny
J.L.Troll
L.Tansl
上述例子均是对一个小文件的域进行修改,因此打印出所有记录查看修改部分不成问题,但如果文件很大,记录甚至超过1 0 0,打印所有记录只为查看修改部分显然不合情理。在模式后面使用花括号将只打印修改部分。取得模式,再根据模式结果实施操作,可能有些抽象,现举一例,只打印修改部分。注意花括号的位置。
J.L.Troll
6. 创建新的输出域
在a w k中处理数据时,基于各域进行计算时创建新域是一种好习惯。创建新域要通过其他域赋予新域标识符。如创建一个基于其他域的加法新域{ $ 4 = $ 2 + $ 3 },这里假定记录包含3个域,则域4为新建域,保存域2和域3相加结果。
在文件g r a d e . t x t中创建新域8保存域目前级别分与域最高级别分的减法值。表达式为‘{ $ 8 = $ 7 - $ 6 }’,语法首先测试域目前级别分小于域最高级别分。新域因此只打印其值大于零的学生名称及其新域值。在B E G I N部分加入t a b键以对齐报告头。
Name Difference
M.Tans 4
J.Lulu 2
Name Difference
M.Tans 4
J.Lulu 2
为增加列数或进行运行结果统计,使用符号+ =。增加的结果赋给符号左边变量值,增加到变量的域在符号右边。例如将$ 1加入变量t o t a l,表达式为t o t a l + = $ 1。列值增加很有用。许多文件都要求统计总数,但输出其统计结果十分繁琐。在a w k中这很简单,请看下面的例子。
将所有学生的‘目前级别分’加在一起,方法是t o t + = $ 6,t o t即为a w k浏览的整个文件的域6结果总和。所有记录读完后,在E N D部分加入一些提示信息及域6总和。不必在a w k中显示说明打印所有记录,每一个操作匹配时,这是缺省动作。
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
L.Tansl 05/99 4712 Brown-2 12 30 28
Club student total points :155
Club student total points :155
在目录中查看文件时,如果想快速查看所有文件的长度及其总和,但要排除子目录,使用ls -l命令,然后管道输出到a w k,a w k首先剔除首字符为d(使用正则表达式)的记录,然后将文件长度列相加,并输出每一文件长度及在E N D部分输出所有文件的长度。
本例中,首先用ls -l命令查看一下文件属性。注意第二个文件属性首字符为d,说明它是一个目录,文件长度是第5列,文件名是第9列。如果系统不是这样排列文件名及其长度,应适时加以改变。
下面的正则表达式表明必须匹配行首,并排除字符d,表达式为^ [ ^ d ]。
使用此模式打印文件名及其长度,然后将各长度相加放入变量t o t中。
...................
total KB:174144
内置的字符串函数
g s u b ( r, s ) 在整个$ 0中用s替代r
g s u b ( r, s , t ) 在整个t中用s替代r
i n d e x ( s , t ) 返回s中字符串t的第一位置
l e n g t h ( s ) 返回s长度
m a t c h ( s , r ) 测试s是否包含匹配r的字符串
s p l i t ( s , a , f s ) 在f s上将s分成序列a
s p r i n t ( f m t , e x p ) 返回经f m t格式化后的e x p
s u b ( r, s ) 用$ 0中最左边最长的子串代替s
s u b s t r ( s , p ) 返回字符串s中从p开始的后缀部分
s u b s t r ( s , p , n ) 返回字符串s中从p开始长度为n的后缀部分
i n d e x(s,t)函数返回目标字符串s中查询字符串t的首位置。l e n g t h函数返回字符串s字符长度。
m a t c h函数测试字符串s是否包含一个正则表达式r定义的匹配。s p l i t使用域分隔符f s将字符串s划分为指定序列a。
s p r i n t函数类似于p r i n t f函数(以后涉及),返回基本输出格式f m t的结果字符串e x p。
s u b(r,s)函数将用s替代$ 0中最左边最长的子串,该子串被( r)匹配。
s u b(s,p)返回字符串s在位置p后的后缀。s u b s t r(s,p,n)同上,并指定子串长度为n。
现在看一看a w k中这些字符串函数的功能。
1. gsub
要在整个记录中替换一个字符串为另一个,使用正则表达式格式, /目标模式/,替换模式/。例如改变学生序号4 8 4 2到4 8 9 9:
[root@Linux_chenwy sam]# awk 'gsub(/4842/,4899){print $0}' grade.txt
J.Troll 07/99 4899 Brown-3 12 26 26
J.Troll 07/99 4899 Brown-3 12 26 26
查询字符串s中t出现的第一位置。必须用双引号将字符串括起来。例如返回目标字符串B u n n y中n y出现的第一位置,即字符个数。
4
返回所需字符串长度,例如检验字符串J . Tr o l l返回名字及其长度,即人名构成的字符个数
7 J.Troll
14
m a t c h测试目标字符串是否包含查找字符的一部分。可以对查找部分使用正则表达式,返回值为成功出现的字符排列数。如果未找到,返回0,第一个例子在A N C D中查找d。因其不存在,所以返回0。第二个例子在A N C D中查找D。因其存在,所以返回A N C D中D出现的首位置字符数。第三个例子在学生J . L u l u中查找u。
0
[root@Linux_chenwy sam]# awk 'BEGIN{print match("ANCD",/D/)}'
4
[root@Linux_chenwy sam]# awk '$1=="J.Lulu" {print match($1,"u")}' grade.txt
4
使用s p l i t返回字符串数组元素个数。工作方式如下:如果有一字符串,包含一指定分隔符- ,例如A D2 - K P 9 - J U 2 - L P - 1,将之划分成一个数组。使用s p l i t,指定分隔符及数组名。此例中,命令格式为( " A D 2 - K P 9 - J U 2 - L P - 1 ",p a r t s _ a r r a y," - "),s p l i t然后返回数组下标数,这里结果为4。
myarray[2]=456
myarray[3]=789
6. sub
使用s u b发现并替换模式的第一次出现位置。字符串S T R包含‘poped popo pill’,执行下列s u b命令s u b(/ o p /," o p ",S T R)。模式o p第一次出现时,进行替换操作,返回结果如下:‘pO Ped pope pill’。
如:学生J . Tr o l l的记录有两个值一样,“目前级别分”与“最高级别分”。只改变第一个为2 9,第二个仍为2 4不动,操作命令为s u b(/ 2 6 /," 2 9 ",$ 0),只替换第一个出现2 4的位置。注意J . Tr o l l记录需存在。
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 29
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 29 26
L.Tansl 05/99 4712 Brown-2 12 30 28
s u b s t r是一个很有用的函数。它按照起始位置及长度返回字符串的一部分。例子如下:
L.T
如果给定长度值远大于字符串长度, a w k将从起始位置返回所有字符,要抽取L Ta n s l - e y的姓,只需从第3个字符开始返回长度为7。可以输入长度9 9,a w k返回结果相同。
L.Tansl
Tans
Lulu
Bunny
Troll
Tansl
GOOD MEN
a w k脚本大多只有一行,其中很少是字符串表示的。大多要求在一行内完成a w k脚本,这一点通过将变量传入a w k命令行会变得很容易。现就其基本原理讲
述一些例子。
使用管道将字符串s t a n d - b y传入a w k,返回其长度。
8
[root@Linux_chenwy sam]# echo $STR|awk '{print substr($STR,1,5)}'
mydoc
[root@Linux_chenwy sam]# echo $STR|awk '{print substr($STR,7)}'
txt
字符串屏蔽序列
使用字符串或正则表达式时,有时需要在输出中加入一新行或查询一元字符。
打印一新行时,(新行为字符/ n),给出其屏蔽序列,以不失其特殊含义,用法为在字符串前加入反斜线。例如使用/ n强迫打印一新行。
如果使用正则表达式,查询花括号( { }),在字符前加反斜线,如/ / { /,将在a w k中失掉其特殊含义。
/ b 退格键
/ t t a b键
/ f 走纸换页
/ d d d 八进制值
/ n 新行
/ c 任意其他特殊字符,例如/ /为反斜线符号
/ r 回车键
May Day
May Day
awk输出函数printf
目前为止,所有例子的输出都是直接到屏幕,除了t a b键以外没有任何格式。a w k提供函数p r i n t f,拥有几种不同的格式化输出功能。例如按列输出、左对齐或右对齐方式。
每一种p r i n t f函数(格式控制字符)都以一个%符号开始,以一个决定转换的字符结束.转换包含三种修饰符。
p r i n t f函数基本语法是p r i n t f([格式控制符],参数),格式控制字符通常在引号里。
printf修饰符
Wi d t h 域的步长,用0表示0步长
. p r e c 最大字符串长度,或小数点右边的位数
表9-7 awk printf格式
% c A S C I I字符
% d 整数
% e 浮点数,科学记数法
% f 浮点数,例如(1 2 3 . 4 4)
% g a w k决定使用哪种浮点数转换e或者f
% o 八进制数
% s 字符串
% x 十六进制数
1. 字符转换
观察A S C I I码中6 5的等价值。管道输出6 5到a w k。p r i n t f进行A S C I I码字符转换。这里也加入换行,因为缺省情况下p r i n t f不做换行动作。
A
A
999.000000
打印所有的学生名字和序列号,要求名字左对齐, 1 5个字符长度,后跟序列号。注意/ n换行符放在最后一个指示符后面。输出将自动分成两列。
M.Tans 48311
J.Lulu 48317
P.Bunny 48
J.Troll 4842
L.Tansl 4712
Name S.Number
M.Tans 48311
J.Lulu 48317
P.Bunny 48
J.Troll 4842
L.Tansl 4712
在查看a w k脚本前,先来查看怎样在a w k命令行中传递变量。
在a w k执行前将值传入a w k变量,需要将变量放在命令行中,格式如下:
下面的例子在命令行中设置变量A G E等于1 0,然后传入a w k中,查询年龄在1 0岁以下的所有学生。
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
的值进行查询测试。
/boot 458589
/dev/shm 99352
/ 2610716
/boot 458589
/dev/shm 99352
在系统中使用df -k命令,产生下列信息:
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda2 5162828 2289804 2610764 47% /
/dev/sda1 497829 13538 458589 3% /boot
none 99352 0 99352 0% /dev/shm
当然可以使用管道将值传入a w k。本例使用w h o命令, w h o命令第一列包含注册用户名,这里打印注册用户,并加入一定信息。
root is logged on
root is logged on
[sam@chenwy sam]$ who
root :0 Nov 23 20:17
root pts/0 Nov 23 20:25 (:0.0)
/usr/sam
可以将a w k脚本写入一个文件再执行它。命令不必很长(尽管这是写入一个脚本文件的主要原因),甚至可以接受一行命令。这样可以保存a w k命令,以使不必每次使用时都需要重新输入。使用文件的另一个好处是可以增加注释,以便于理解脚本的真正用途和功能。
使用前面的几个例子,将之转换成a w k可执行文件。像原来做的一样,将学生目前级别分相加awk ‘(t o t + = $ 6) END{print "club student total points:" t o t }’ g r a d e . t x t。
创建新文件s t u d e n t _ t o t . a w k,给所有a w k程序加入a w k扩展名是一种好习惯,这样通过查看文件名就知道这是一个a w k程序。文本如下:
#!/bin/awk -f
#all commnet lines must start with a hash '#'
#name:students_tots.awk
#to call:student_tot.awk grade.txt
#prints total and average of club student points
#print a header first
BEGIN{
print "Student Date Member No. Grade Age Points Max"
print "Name Joined Gained Point Available"
print "=============================================================="
}
#let's add the scores of points gained
(tot+=$6)
#finished proessing now let's print the total and average point
END{
print "Club student total points :" tot
print "Average Club Student Points:" tot/NR}
信息和结尾的平均值。基本上这是一个一行脚本文件。
执行时,在脚本文件后键入输入文件名,但是首先要对脚本文件加入可执行权限。
[sam@chenwy sam]$./student_tot.awk grade.txt
Student Date Member No. Grade Age Points Max
Name Joined Gained Point Available
==============================================================
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
L.Tansl 05/99 4712 Brown-2 12 30 28
Club student total points :155
Average Club Student Points:31
如有一个文件strip中有多条重复错误提法:
etreiytrpytyu
ERROR*
ERROR*
ERROR*
ERROR*
IUEWROPYJRTMELUYK
ERROR*
ERROR*
ERROR*
ERROR*
ERROR*
ERROR*
EWUTIRWJYHT
ERROR*
ERROR*
JGIOERYO56ERU
ERROR*
ERROR*
ERROR*
JGEORYKP65EKU;YK,
#!/bin/awk -f
#error_strip.awk
#to call:error_strip.awk<filename>
#strips out the ERROR* lines if there are more than one
#ERROR* lines after each failed record.
BEGIN {error_line=""}
#tell awk the whole is "ERROR*"
{if ($0=="ERROR*" && error_line=="ERROR*")
#go to next line
next;
error_line=$0;print}
etreiytrpytyu
ERROR*
IUEWROPYJRTMELUYK
ERROR*
EWUTIRWJYHT
ERROR*
JGIOERYO56ERU
ERROR*
JGEORYKP65EKU;YK,
5. 在a w k中使用F S变量
如果使用非空格符做域分隔符( F S)浏览文件,例如# 或:,编写这样的一行命令很容易,因为使用F S选项可以在命令行中指定域分隔符。
下述脚本指定F S变量。脚本从/ e t c / p a s s w d文件中抽取第1和第5域,通过分号“;”分隔p a s s w d文件域。第1域是帐号名,第5域是帐号所有者。
我举的例子是第七个域:
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
.................................
现使用脚本如下:
#!/bin/awk -f
#to call:passwd.awk /etc/passwd
#print out the first and seventh fields
BEGIN{
FS=":"}
{print $1,"/t",$7}
[sam@Linux_chenwy sam]$ ./passwd.awk passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
.......................................
向a w k脚本传值与向a w k一行命令传值方式大体相同,格式为:
脚本如下:
#!/bin/awk -f
#check on how many fields in a file
#name:fieldcheck.awk
#to call:fieldcheck MAX=n FS=<separator> filename
#
NF!=MAX{
print("line" NR " does not have " MAX "fields")}
如果以/ e t c / p a s s w d作输入文件(p a s s w d文件有7个域),运行上述脚本。参数格式如下:
[sam@Linux_chenwy sam]$ ./fieldcheck.awk MAX=7 FS=":" passwd
使用前面一行脚本的例子,将之转换成a w k脚本如下:
#!/bin/awk -f
#name:age.awk
#to call:age.awk AGE=n grade.txt
#print ages that are lower than the age supplied on the comand line
{if ($5<AGE)
print $0}
不要忘了增加脚本的可执行权限,然后将变量和赋值放在命令行脚本名字后、输入文件前执行。
[sam@Linux_chenwy sam]$ ./name.awk AGE=10 grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
#!/bin/awk -f
#to call:du|duawk.awk
#prints file/direc's in bytes and blocks
BEGIN{
OFS="/t";
print "name" "/t/t","bytes","blocks/n"
print "==============================="}
{print $2,"/t/t",$1*512,$1}
12 ./.kde/Autostart
16 ./.kde
8 ./.xemacs
4 ./sam
4 ./dir1
4 ./file6
184 .
name bytes blocks
===============================
./.kde/Autostart 6144 12
./.kde 8192 16
./.xemacs 4096 8
./sam 2048 4
./dir1 2048 4
./file6 2048 4
. 94208 184
前面讲述s p l i t函数时,提到怎样使用它将元素划分进一个数组。这里还有一个例子:
3
Myarray[2]="456"
Myarray[3]="789"
#!/bin/awk -f
#name:arraytest.awk
#prints out an array
BEGIN{
record="123#456#789";
split(record,myarray,"#")}
END{for (i in myarray) {print myarray[i]}}
[sam@Linux_chenwy sam]$ ./arraytest.awk /dev/null
123
456
789
[sam@Linux_chenwy sam]$
上面的例子讲述怎样通过s p l i t函数使用数组。也可以预先定义数组,并使用它与域进行比较测试,下面的例子中将使用更多的数组。
下面是从空手道数据库卸载的一部分数据,包含了学生级别及是否是成人或未成年人的信息,有两个域,分隔符为( #),文件如下:
Yellow#Junior
Orange#Senior
Yellor#Junior
Purple#Junior
Brown-2#Junior
White#Senior
Orange#Senior
Red#Junior
Red#Junior
Brown-2#Senior
Yellow#Senior
Red#Junior
Blue#Senior
Green#Senior
Purple#Junior
White#Junior
1) 俱乐部中Ye l l o w、O r a n g e和R e d级别的人各是多少。
2 ) 俱乐部中有多少成年人和未成年人。
查看文件,也许2 0秒内就会猜出答案,但是如果记录超过6 0个又怎么办呢?这不会很容易就看出来,必须使用a w k脚本。
首先看看a w k脚本,然后做进一步讲解。
#!/bin/awk -f
#name:belts.awk
#to call:belts.awk grade2.txt
#loops through the grade2.txt file and counts how many
#belts we have in (yellow,orange,red)
#also count how many adults and juniors we have
#
#start of BEGIN
#set FS and load the arrays with our values
#B E G I N部分设置F S为符号#,即域分隔符
BEGIN{FS="#"
#Load the belt colours we are interested in only
#因为要查找Ye l l o w、O r a n g e和R e d三个级别。
#然后在脚本中手工建立数组下标对学生做同样的操作。
#注意,脚本到此只有下标或元素,并没有给数组名本身加任何注释。
belt["Yellow"]
belt["Orange"]
belt["Red"]
#end of BEGIN
#load the student type
student["Junior"]
student["Senior"]
}
##初始化完成后, B E G I N部分结束。记住B E G I N部分并没有文件处理操作。
#loop thru array that holds the belt colours against field-1
#if we have a match,keep a running total
#现在可以处理文件了。
#首先给数组命名为c o l o r,使用循环语句测试域1级别列是否
#等于数组元素之一(Ye l l o w、O r a n g e或R e d),
#如果匹配,依照匹配元素将运行总数保存进数组。
{for (colour in belt)
{if($1==colour)
belt[colour]++}}
#loop thru array that holds the student type against
#field-2 if we have a match,keep a runing total
#同样处理数组‘ S e n i o r _ o r _ j u n i o r’,
#浏览域2时匹配操作满足,运行总数存入j u n i o r或s e n i o r的匹配数组元素。
{for (senior_or_junior in student)
{if ($2==senior_or_junior)
student[senior_or_junior]++}}
#finished processing so print out the matches..for each array
#E N D部分打印浏览结果,对每一个数组使用循环语句并打印它。
END{for (colour in belt )print "The club has ",belt[colour],colour,"Belts"
#注意在打印语句末尾有一个/符号,用来通知a w k(或相关脚本)命令持续到下一行,
#当输入一个很长的命令,并且想分行输入时可使用这种方法。
for (senior_or_junior in student) print "The club has ",/
student[senior_or_junior],senior_or_junior,"student"}
[sam@Linux_chenwy sam]$ ./belts.awk grade_student.txt
The club has 3 Red Belts
The club has 2 Orange Belts
The club has 2 Yellow Belts
The club has 7 Senior student
The club has 9 Junior student
" 抽取域。
" 匹配正则表达式。
" 比较域。
" 向a w k传递参数。
" 基本的a w k行操作和脚本。
1 调用awk
有三种方式调用a w k,第一种是命令行方式,如:
上面例子中, [ - F域分隔符]是可选的,因为a w k使用空格作为缺省的域分隔符,因此如果要浏览域间有空格的文本,不必指定这个选项,但如果要浏览诸如p a s s w d文件,此文件各域以冒号作为分隔符,则必须指明- F选项,如:
第三种方式是将所有的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读文件记录的方式
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
任何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将打印出所有浏览出来的记录。