专职C++

不能停止的脚步

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  163 Posts :: 7 Stories :: 135 Comments :: 0 Trackbacks

常用链接

留言簿(28)

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜

原文:http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/index.html 
目录

6.1. gawk上路

6.1.1. 什么是gawk?

6.1.2. Gawk命令

6.2. 打印程序

6.2.1. 打印选择的域

6.2.2. 格式化域

6.2.3. print命令和正则表达式

6.2.4. 特殊的patterns

6.2.5. Gawk脚本

6.3. Gawk变量

6.3.1. 输入域的分隔符

6.3.2. 输出分隔符

6.3.3. 记录的数量

6.3.4. 用户定义的变量

6.3.5. 更多例子

6.3.6. printf程序

6.4. 总结

6.5. 练习

摘要

本章我们会讨论:

  • 什么是 gawk
  • 在命令行中使用 gawk 命令
  • 怎么使用 gawk 来格式化文本
  • gawk 怎么使用则正则表达式
  • 脚本中的 gawk
  • gawk 和变量

把它变得更有趣味

就像sed一样,整本书写了多个版本的awk。这个介绍远还不够完整,且只是为了能够理解在后面章节中的例子。要得到更多的信息,最好是从 GNU awk 的随附文档开始:“GAWK: Effective AWK Programming: A User's Guide for GNU Awk”.

6.1. gawk上路

6.1.1. 什么是gawk?

Gawk 是通常在UNIX系统下使用的另外一个流行的流编辑器 awk 的GNU版本。尽管 awk 程序常常只是一个 gawk 的连接,但是我们还是称作它为 awk

awk 的最基本的作用是搜索含有1个或者多个模板的文件中的行或者其他文本块。当一行符合搜索的patterns,在该行就会实现指定的动作。

在 awk 中的程序和许多其他语言中的程序是不一样的,因为 awk 程序是 “数据驱动的” :你先描述你想处理的数据,然后当找到它们的时候怎么处理。许多其他的语言是“过程的”。你需要具体的描述程序该采取的措施。当使用过程化的语言时,通常更难清楚地描述你需要处理的数据。正因为如此, awk 程序经常能清爽容易地读写。

6.1.2. Gawk命令

当你运行 awk 的时候,你指定一个 awk 程序 来通知 awk 该如何做。程序由一系列 规则 组成。(也可能包含函数定义,循环,条件和其他程序结构,高级特性等一系列我们已经遗忘的东西。)每个规则指定了搜索的模板和在搜索到一个模板后执行的动作。

有几种运行 awk 的方法。如果程序很短,最方便的是在命令行运行它:

awk PROGRAM inputfile(s)

在已经完成的多个改变,通常在多个文件,很方便把 awk 命令放到脚本当中,读起来像这样:

awk -f PROGRAM-FILE inputfile(s)

 

源文档 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06.html>

6.2. 打印程序

6.2.1. 打印选择的域

awk 中的 print 命令把输入文件中选择的数据输出。

当 awk 读取文件的一行,根据指定的 输入域分隔符 把行分开, FS,awk 的一个环境变量(见 第 6.3.2 节 “输出分隔符”)。它被预先定义为一个或者多个空格或者制表符。

变量 $1, $2, $3, ..., $N 把输入行的第一第二第三直到最后一个域保存起来。变量 $0 把整行的值保存起来。在下面的图片描述中,我们看到 df 命令输出有6栏。

图 6.1. awk中的块

在 ls -l 的输出,有9栏。 print 语句像这样使用这些域:

kelly@octarine ~/test> ls -l | awk '{ print $5 $9 }'
160orig
121script.sed
120temp_file
126test
120twolines
441txt2html.sh

kelly@octarine ~/test>

这个命令把包含文件大小的第五栏和包含文件名的最后一栏打印出来。除非你使用正式的方法来用一个逗号来分割你想打印的列,否则输出并不十分适合阅读。这样的情况下,默认的输出分割字符,通常是一个空格,将被放入每个输出域。

6.2.2. 格式化域

没有格式化,只使用输出分隔符的话看上去比较破,插入几个制表符和指示输出的标记会使它变得漂亮很多:

kelly@octarine ~/test> ls -ldh * | grep -v total | \
awk
'{ print "Size is " $5 " bytes for " $9 }'
Size is 160 bytes for orig
Size is 121 bytes for script.sed
Size is 120 bytes for temp_file
Size is 126 bytes for test
Size is 120 bytes for twolines
Size is 441 bytes for txt2html.sh

kelly@octarine ~/test>

注意反斜杠的用法,让shell不把它翻译成分隔命令而使其在下一行继续很长的输入。虽然命令行的输入实际上是没有长度限制的,但是你的显示器不是,打印的纸当然也不是。使用反斜杠也允许拷贝和粘贴以上行到一个终端窗口。

ls 的 -h 选项用来支持把大文件的字节数转换成更容易读的格式。当把目录作为参数的时候长列表的输出显示目录中快的总数。这行对我们并没有什么用处,所以我们加上一个*。同样的原因我们同样加上 -d 选项,万一*对一个目录展开。

在这个例子中的反斜杠提示了一行的延长。见 第 3.3.2 节 “转义字符”

甚至在反序中你也能取出任何栏的的数字。下面的例子证明了最危险的区分。

kelly@octarine ~> df -h | sort -rnk 5 | head -3 | \
awk
'{ print "Partition " $6 "\t: " $5 " full!" }'
Partition /var  : 86% full!
Partition /usr  : 85% full!
Partition /home : 70% full!

kelly@octarine ~>

下表给出了特殊格式化字符的总揽:

表 6.1. gawk的格式化字符

转义字符

含义

\a

Bell character

\n

Newline character

\t

Tab

引用,$和其他元字符应该使用反斜杠来进行转义。

6.2.3. print命令和正则表达式

用斜杠把正则表达式包含起来可以当作一个pattern。然后正则表达式测试整个文本的每条记录。语法如下:

awk 'EXPRESSION { PROGRAM }' file(s)

下面的例子现实了只有本地磁盘设备的信息,网络文件系统没有显示:

kelly is in ~> df -h | awk '/dev\/hd/ { print $6 "\t: " $5 }'
/       : 46%
/boot   : 10%
/opt    : 84%
/usr    : 97%
/var    : 73%
/.vol1  : 8%

kelly is in ~>

斜杠也需要转义,因为对于 awk 它们有着特殊的含义。

下面的另外一个例子是我们在 /etc 目录搜索以 “.conf” 结尾和 “a” 或者 “x” 开头的文件,使用扩展的正则表达式:

kelly is in /etc> ls -l | awk '/\<(a|x).*\.conf$/ { print $9 }'
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf

kelly is in /etc>

这个例子说明了在正则表达式中.的特殊意义。第一个表明了我们想要搜索在第一个搜索字符串之后的任何字符,第二个因为是要查找字符串的一部分所以被转义了(文件名的结束)。

6.2.4. 特殊的patterns

为了在输出之前加上注释,使用BEGIN语句:

kelly is in /etc> ls -l | \
awk
'BEGIN { print "Files found:\n" } /\<[a|x].*\.conf$/ { print $9 }'
Files found:
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf

kelly is in /etc>

加上 END 语句能插入文本在整个输入被处理之后:

kelly is in /etc> ls -l | \
awk
'/\<[a|x].*\.conf$/ { print $9 } END { print \
"Can I do anything else for you, mistress?" }'

amd.conf
antivir.conf
xcdroast.conf
xinetd.conf
Can I do anything else for you, mistress?

kelly is in /etc>

6.2.5. Gawk脚本

由于往往命令都有点长,你可能想把他们放到脚本中,来重用它。一个 awk 脚本包含定义pattern和动作的awk语句。

作为一个说明,我们将建立一个报告来显示占用率最高的分区,参见 第 6.2.2 节 “格式化域”

kelly is in ~> cat diskrep.awk
BEGIN { print "*** WARNING WARNING WARNING ***" }
/\<[8|9][0-9]%/ { print "Partition " $6 "\t: " $5 " full!" }
END { print "*** Give money for new disks URGENTLY! ***" }

kelly is in ~> df -h | awk -f diskrep.awk
*** WARNING WARNING WARNING ***
Partition /usr  : 97% full!
*** Give money for new disks URGENTLY! ***

kelly is in ~>

awk 先打印一个开始信息,然后格式化所有包含一个8或者9开头的词的行,然后后接一个数字和百分符号,然后加入结束信息。

语法高亮

Awk是一个编程语言。他的语法能被大多数能对其他语言比如c,bash,HTML进行语法高亮编辑器所识别。

 

源文档 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06s02.html>

 

6.3. Gawk变量

既然 awk 处理输入文件,他使用几个变量。一些是可以编辑的,一些是只读的。

6.3.1. 输入域的分隔符

域分隔符,既不是一个单独的字符也不是一个普通的表达式,是控制 awk 把一个输入分割成几个域。输入记录按分割定义进行字符顺序扫描;域就是在相符的那些文字中间的那部分。

域分隔符代表内建的变量 FS,注意POSIX标准的shell使用的变量 IFS 和他是有一些区别的。

域分隔符变量的值可以在 awk 程序中用赋值操作符 = 来改变。通常最好的执行时间是一开始也就是还没有处理任何输入的时候,因此第一个记录就被随合适的分隔符一起读取。要这么做,清使用特殊的 BEGIN pattern。

以下的例子,我们编制了一条命令来显示系统里的所有用户及其描述:

kelly is in ~> awk 'BEGIN { FS=":" } { print $1 "\t" $5 }' /etc/passwd
--output omitted--
kelly        Kelly Smith
franky        Franky B.
eddy        Eddy White
willy        William Black
cathy        Catherine the Great
sandy        Sandy Li Wong

kelly is in ~>

在一个 awk 脚本中,它看起来像这样:

kelly is in ~> cat printnames.awk
BEGIN { FS=":" }
{ print $1 "\t" $5 }

kelly is in ~> awk -f printnames.awk /etc/passwd
--output omitted--

小心地选择输入分隔域来防止出现问题。一个例子来说明这个:说你需要输入想这样的行的形式:

“Sandy L. Wong, 64 Zoo St., Antwerp, 2000X”

你这样写了一个打印出记录中人的名字的命令行或者是脚本:

awk 'BEGIN { FS="," } { print $1, $2, $3 }' inputfile

但是可能一个人有PhD,而且可能写成这样:

“Sandy L. Wong, PhD, 64 Zoo St., Antwerp, 2000X”

你的 awk 会给出错误的输出。需要的话,使用额外的一个 awk 或者 sed 来统一数据输出的格式。

默认的输入分隔符是一个或多个空格和制表符。

6.3.2. 输出分隔符

6.3.2.1. 输出域分隔符

在输出中域通常被空格分隔。当你对 print 命令使用正确的语法且字段用逗号分隔时,这将很明显的:

kelly@octarine ~/test> cat test
record1         data1
record2         data2

kelly@octarine ~/test> awk '{ print $1 $2}' test
record1data1
record2data2

kelly@octarine ~/test> awk '{ print $1, $2}' test
record1 data1
record2 data2

kelly@octarine ~/test>

如果你不输入逗号, print 将把输出的项目全部当成一个字段,因此省略默认输出 分隔符, OFS的使用。

在摄制了这个内建变量的值之后任何字符串可以被用作一个输出域。

6.3.2.2. 输出记录分隔符

整个 print 语句的输出叫做输出记录。每个在输出记录里的print命令的结果,输出一个叫做 输出记录分隔符 ORS的字符串。这个变量的默认值是 “\n”,一个换行符。因此,每个 print 语句生成一个单独的行。

要改变输出域和记录的,只要给 OFS 和 ORS 赋新的值:

kelly@octarine ~/test> awk 'BEGIN { OFS=";" ; ORS="\n-->\n" } \
{ print $1,$2}'
test
record1;data1
-->
record2;data2
-->

kelly@octarine ~/test>

如果 ORS 的值不包含换行,那么程序中的输出就会输出成一个单独行。

6.3.3. 记录的数量

内建的 NR 包含了处理过的记录的数量。在读入一个新的输入行之后他会自行增加一次。你可以用它来计算记录的总数,或者在每个输出记录中:

kelly@octarine ~/test> cat processed.awk
BEGIN { OFS="-" ; ORS="\n--> done\n" }
{ print "Record number " NR ":\t" $1,$2 }
END { print "Number of records processed: " NR }

kelly@octarine ~/test> awk -f processed.awk test
Record number 1:        record1-data1
--> done
Record number 2:        record2-data2
--> done
Number of records processed: 2
--> done

kelly@octarine ~/test>

6.3.4. 用户定义的变量

除了内建的变量之外,你也可以定义自己的变量。当 awk 碰到一个不存在的变量(没有事先定义的)的引用时,这个变量就被创建并且使用一个空字符串进行赋值。对于后来所有的引用,就是该变量最后被赋予的那个值。变量可以是一个字符串或者一个数字。输入域的内容也可以被赋予变量。

值可以直接用 = 来赋值,或者你可以使用现有变量的值和其他操作符组合:

kelly@octarine ~> cat revenues
20021009        20021013        consultancy     BigComp         2500
20021015        20021020        training        EduComp         2000
20021112        20021123        appdev          SmartComp       10000
20021204        20021215        training        EduComp         5000

kelly@octarine ~> cat total.awk
{ total=total + $5 }
{ print "Send bill for " $5 " dollar to " $4 }
END { print "---------------------------------\nTotal revenue: " total }

kelly@octarine ~> awk -f total.awk test
Send bill for 2500 dollar to BigComp
Send bill for 2000 dollar to EduComp
Send bill for 10000 dollar to SmartComp
Send bill for 5000 dollar to EduComp
---------------------------------
Total revenue: 19500

kelly@octarine ~>

类似于C的简写 VAR+= value 也是可以接受的。

6.3.5. 更多例子

当我们使用 awk 脚本时, 第 5.3.2 节 “写输出文件” 的例子会变得更加容易:

kelly@octarine ~/html> cat make-html-from-text.awk
BEGIN { print "<html>\n<head><title>Awk-generated HTML</title></head>\n<body bgcolor=\"#ffffff\">\n<pre>" }
{ print $0 }
END { print "</pre>\n</body>\n</html>" }

而且当用 awk 来替代 sed 后,命令也变得更加直截了当:

kelly@octarine ~/html> awk -f make-html-from-text.awk testfile > file.html

在你系统上的awk例子。

我们再次回到包含你系统启动脚本的目录。输入一个和以下相似的命令来查看更多 awk 命令的使用方法:

grep awk /etc/init.d/*

6.3.6. printf程序

为了更精确的控制 print 提供的正常输出格式,可以使用 printf。 printf命令可以用来指明每个项目使用的域的宽度,同时也有用于数字的多种格式选择。这一切只要增加一个字符串,叫做格式字符串,控制怎样和那里来打印那些项目。

语法也和C语言的 printf 语句相似;请察看你的C介绍手册。 gawk 信息页面包含完整的解释。

 

源文档 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06s03.html>

 

6.4. 总结

gawk 工具解释特殊目的的编程语言,处理简单的数据重新格式化的工作,只需要几行代码。他是通常UNIX awk 命令的免费版本。

这个工具从输入数据读取行且能够方便的识别。 print 是最普通的来过滤和格式化定义的域的程序。

闲置的变量可以直接声明也允许在处理输入流的同时进行简单的计算求和,统计和其他运算。变量和命令可以放到 awk 脚本来进行后台处理。

 

源文档 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06s04.html

posted on 2012-03-02 13:59 冬瓜 阅读(539) 评论(0)  编辑 收藏 引用 所属分类: 转贴

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理