shell学习笔记1---awk基础(原创)

本文涉及的产品
.cn 域名,1个 12个月
简介:

awk概述
awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数和 动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk的处理文本和数据的方式是这 样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出 (屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。下面介绍的awk是以GUN的gawk为例的,在 linux系统中已把awk链接到gawk,所以下面全部以awk进行介绍。

awk概念

模式和动作
任何a w k语句都由模式和动作组成。在一个a w k脚本中可能有许多语句。模式部分决定动
作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保
持执行状态。
模式可以是任何条件语句或复合语句或正则表达式。模式包括两个特殊字段BEGIN和E N D。使用B E G I N语句设置计数和打印头。B E G I N语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行。END语句用来在a w k完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明模式,a w k总是匹配或打印行数。实际动作在大括号{ }内指明。动作大多数用来打印,但是还有些更长的代码诸如i f和循环(looping)语句及循环退出结构。如果不指明采取动作,a w k将打印出所有浏览出来的记录
记录
awk把每一个以换行符结束的行称为一个记录。
记录分隔符:默认的输入和输出的分隔符都是回车,保存在内建变量ORS和RS中。
$0变量:它指的是整条记录。如$ awk '{print $0}' test将输出test文件中的所有记录。
变量NR:一个计数器,每处理完一条记录,NR的值就增加1。如$ awk '{print NR,$0}' test将输出test文件中所有记录,并在记录前显示记录号。

记录中每个单词称做“域”,默认情况下以空格或tab分隔。awk可跟踪域的个数,并在内建变量NF中保存该值。如$ awk '{print $1,$3}' test将打印test文件中第一和第三个以空格分开的列(域)。
域分隔符
内建变量FS保存输入域分隔符的值,默认是空格或tab。我们可以通过-F命令行选项修改FS的值。如$ awk -F: '{print $1,$5}' test将打印以冒号为分隔符的第一,第五列的内容。
符写成放到方括号中,如$awk -F'[:\t]' '{print $1,$3}' test,表示以空格、冒号和tab作为分隔符。
输出域的分隔符默认是一个空格,保存在OFS中。如$ awk -F: '{print $1,$5}' test,$1和$5间的逗号就是OFS的值。

域和记录操作
真正执行前看几个例子,现有一文本文件grade.txt,记录了一个称为柔道数据库的行信息。
$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 26
L.Tansl 05/99   4712 Brown-2 12 30 28
此文本文件有7个域,即(1)名字、(2)升段日期、(3)学生序号、(4)腰带级别、(5)年龄、(6)目前比赛积分、(7)比赛最高分。
因为域间使用空格作为域分隔符,故不必用- F选项划分域,现浏览文件并导出一些数据。在例子中为了利于显示,将空格加宽使各域看得更清晰。

1、保存awk输出
有两种方式保存shell提示符下awk脚本的输出。最简单的方式是使用输出重定向符号>文件名,下面的例子重定向输出到文件wow。
$awk "{print $0}" grade.txt >wow
$cat grade.txt
使用这种方法要注意,显示屏上不会显示输出结果。因为它直接输出到文件。只有在保证输出结果正确时才会使用这种方法。它也会重写硬盘上同名数据。
第二种方法是使用tee命令,在输出到文件的同时输出到屏幕。在测试输出结果正确与否时多使用这种方法。例如输出重定向到文件delete_me_and_die,同时输出到屏幕。使用这种方法,在awk命令结尾写入| tee delete_me_and_die。
$awk "{print $0}" grade.txt | tee delete_me_and_die
2. 使用标准输入
在深入讲解这一章之前,先对awk脚本的输入方法简要介绍一下。实际上任何脚本都是从标准输入中接受输入的。为运行本章脚本,使用awk脚本输入文件格式,例如:belts.awk grade_student.txt
也可替代使用下述格式:
使用重定向方法:
belts.awk < grade2.txt
或管道方法:
grade2.txt | belts.awk

3 打印所有记录
$awk '{print $0}' grade.txt
awk读每一条记录。因为没有模式部分,只有动作部分{print $0}(打印所有记录),这个动作必须用花括号括起来。上述命令打印整个文件。
4. 打印单独记录
假定只打印学生名字和腰带级别,通过查看域所在列,可知为field-1和field-4,因此可以使用$1和$4,但不要忘了加逗号以分隔域。
$awk '{print $1,$4}' grade.txt
M.Tans Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown-3
L.Tansl Brown-2
5. 打印报告头
上述命令输出在名字和腰带级别之间用一些空格使之更容易划分,也可以在域间使用tab键加以划分。为加入tab键,使用tab键速记引用符\t,后面将对速记引用加以详细讨论。也可以为输出文本加入信息头。本例中加入name和belt及下划线。下划线使用\n,强迫启动新行,并在\n下一行启动打印文本操作。打印信息头放置在BEGIN模式部分,因为打印信息头被界定为一个动作,必须用大括号括起来。在awk查看第一条记录前,信息头被打印。
$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-3
L.Tansl  Brown-2
6. 打印信息尾
如果在末行加入end of report信息,可使用END语句。END语句在所有文本处理动作执行完之后才被执行。END语句在脚本中的位置放置在主要动作之后。下面简单打印头信息并告之查询动作完成。
$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
end-of-report
7. awk错误信息提示
几乎可以肯定,在使用awk时,将会在命令中碰到一些错误。awk将试图打印错误行,但由于大部分命令都只在一行,因此帮助不大。
系统给出的显示错误信息提示可读性不好。使用上述例子,如果丢了一个双引号, awk将返回:
$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
当第一次使用awk时,可能被错误信息搅得不知所措,但通过长时间和不断的学习,可总结出以下规则。在碰到awk错误时,可相应查找:
确保整个awk命令用单引号括起来。
确保命令内所有引号成对出现。
确保用花括号括起动作语句,用圆括号括起条件语句。
可能忘记使用花括号,也许你认为没有必要,但awk不这样认为,将按之解释语法。
如果查询文件不存在,将得到下述错误信息:
$awk 'END {print NR}' grades.txt
awk: cmd. line:2: fatal: cannot open file 'grades.txt' for reading (没有那个文件或目录)
8.awk 键盘输入
如果在命令行并没有输入文件grade.txt,将会怎样?
$awk 'BEGIN {print "Name\n--------"}{print $1} END {"end-of-report"}'
Name
--------
BEGIN部分打印了文件头,但awk最终停止操作并等待,并没有返回shell提示符。这是因为awk期望获得键盘输入。因为没有给出输入文件,awk假定下面将会给出。如果愿意,顺序输入相关文本,并在输入完成后敲<Ctrl-D>键。如果敲入了正确的域分隔符,awk会像第一个例子一样正常处理文本。这种处理并不常用,因为它大多应用于大量的打印稿。
元字符
这里是awk中正则表达式匹配操作中经常用到的字符。
\ ^ $. [] | () * + ?
这里有两个字符只适用于awk而不适用于grep或sed。它们是:
+ 使用+匹配一个或多个字符。
?匹配模式出现频率。例如使用/X Y?Z/匹配X Y Z或Y Z。
条件操作符
awk条件操作符

操作符描述 操作符描述
< 小于
>= 大于等于
<= 小于等于
== 等于
~ 匹配正则表达式
!~ 不匹配正则表达式
!= 不等于

1.匹配
为使一域号匹配正则表达式,使用符号'~'后紧跟正则表达式,也可以用if语句。awk中if后面的条件用()括起来。
观察文件grade.txt,如果只要显示brown腰带级别可知其所在域为field-4,这样可以写出表达式{if($4~/brown/) print}意即如果field-4包含brown,打印它。如果条件满足,则打印匹配记录行。可以编写下面脚本,因为这是一个动作,必须用花括号{ }括起来。
# awk '{if($4~/Brown/) print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
匹配记录找到时,如果不特别声明, awk缺省打印整条记录。使用if语句开始有点难,但不要着急,因为有许多方法可以跳过它,并仍保持同样结果。下面例子意即如果记录包含模式brown,就打印它:
# awk '$0 ~ /Brown/' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
2.精确匹配
假定要使字符串精确匹配,比如说查看学生序号48,文件中有许多学生序号包含48,如果在field-3中查询序号48,awk将返回所有序号带48的记录:
# 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
为精确匹配48,使用等号==,并用单引号括起条件。例如$3
# awk '$3=="48" {print$0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
# awk '{if($3=="48") print$0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
3.不匹配
有时要浏览信息并抽取不匹配操作的记录,与~相反的符号是!~,意即不匹配。像原来使用查询brown腰带级别的匹配操作一样,现在看看不匹配情况。表达式$0 !~/brown/,意即查询不包含模式brown腰带级别的记录并打印它。
注意,缺省情况下, awk将打印所有匹配记录,因此这里不必加入动作部分。
# 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
可以只对field-4进行不匹配操作,方法如下:
# awk '{if($4~/Brown/) print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99 4712 Brown-2 12 30 28
如果只使用命令awk$4 !="brown"{print $0} grade.txt,将返回错误结果,因为用引号括起了brown,将只匹配'brown而不匹配brown-2和brown-3,当然,如果想要查询非brown-2的腰带级别,可做如下操作:
# 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.小于
看看哪些学生可以获得升段机会。测试这一点即判断目前级别分field-6是否小于最高分field-7,在输出结果中,加入这一改动很容易。
# 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.小于等于
对比小于,小于等于只在操作符上做些小改动,满足此条件的记录也包括上面例子中的输出情况。
# awk '{if($6 <= $7) print $1}' grade.txt
M.Tans
J.Lulu
J.Troll
6.大于
# awk '{if($6 > $7) print $1}' grade.txt
P.Bunny
L.Tansl
7.设置大小写
为查询大小写信息,可使用[ ]符号。在测试正则表达式时提到可匹配[ ]内任意字符或单词,因此若查询文件中级别为green的所有记录,不论其大小写,表达式应为'/[Gg]reen/'
# 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,尖角符号代表行首。
# awk '$1 ~ /^...a/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
L.Tansl 05/99 4712 Brown-2 12 30 28
9.或关系匹配
为抽取级别为yellow或brown的记录,使用竖线符|。意为匹配|两边模式之一。注意,使用竖线符时,语句必须用圆括号括起来。
# awk '$0 ~/(Yellow|Brown)/' grade.txt
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
上面例子输出所有级别为Ye l l o w或brown的记录。
使用这种方法在查询级别为Green或green时,可以得到与使用[ ]表达式相同的结果。
# awk '/^M/' grade.txt
M.Tans 5/99 48311 Green 8 40 44
行首
不必总是使用域号。如果查询文本文件行首包含M的代码,可简单使用下面^符号:
# awk '/^M/' grade.txt
复合表达式即为模式间通过使用下述各表达式互相结合起来的表达式:
&& AND : 语句两边必须同时匹配为真。
|| OR:语句两边同时或其中一边匹配为真。
! 非求逆
10. AND
打印记录,使其名字为'P.Bunny且级别为Yellow,使用表达式($1=="P.Bunny" && $4=="Yellow"),意为&&两边匹配均为真。完整命令如下:
# awk '{if ($1=="P.Bunny" && $4=="Yellow") print $0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
11. Or
如果查询级别为Yellow或brown,使用或命令。意为"||"符号两边的匹配模式之一或全部为真。
# 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 26
L.Tansl 05/99 4712 Brown-2 12 30 28
原来不一定得加print,下面我自己对例一二做了一下
1
# 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
2
# awk '$3=="48"' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
# 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 26
L.Tansl 05/99 48 Brown-2 12 30 28
2中,我把=和==写错了,呵呵,一个是赋值,一个是等于
awk内置变量
awk有许多内置变量用来设置环境信息。这些变量可以被改变。
ARGC:命令行参数个数
ARGV:命令行参数排列

ARGIND 当前被处理文件的ARGV标志符
ENVIRON:支持队列中系统环境变量的使用
FILENAME:awk浏览的文件名
FNR:浏览文件的记录数
FS:设置输入域分隔符,等价于命令行-F选项
NF:浏览记录的域个数
NR:已读的记录数
OFS:输出域分隔符
ORS:输出记录分隔符
RS:控制记录分隔符
ARGC支持命令行中传入awk脚本的参数个数。ARGV是ARGC的参数排列数组,其中每一元素表示为ARGV [n],n为期望访问的命令行参数。
ENVIRON支持系统设置的环境变量,要访问单独变量,使用实际变量名,例如ENVIRON ["EDITOR"]="Vi"。
FILENAME支持awk脚本实际操作的输入文件。因为awk可以同时处理许多文件,因此如果访问了这个变量,将告之系统目前正在浏览的实际文件。
FNR支持awk目前操作的记录数。其变量值小于等于NR。如果脚本正在访问许多文件,每一新输入文件都将重新设置此变量。
FS用来在awk中设置域分隔符,与命令行中-F选项功能相同。缺省情况下为空格。如果用逗号来作域分隔符,设置FS=","。
NF支持记录域个数,在记录被读之后再设置。
OFS允许指定输出域分隔符,缺省为空格。如果想设置为#,写入OFS="#"。
ORS为输出记录分隔符,缺省为新行(\n)。
RS是记录分隔符,缺省为新行(\n)。
NF、NR和FILENAME
要快速查看记录个数,应使用NR。比如说导出一个数据库文件后,如果想快速浏览记录个数,以便对比于其初始状态,查出导出过程中出现的错误。使用NR将打印输入文件的记录个数。print NR放在END语法中。
# awk 'END{print NR}' grade.txt
5
如:所有学生记录被打印,并带有其记录号。使用NF变量显示每一条读记录中有多少个域,并在END部分打印输入文件名。
# awk '{print NF,NR,$0} END{print FILENAME}' grade.txt
7 1 M.Tans 5/99 48311 Green 8 40 44
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
在从文件中抽取信息时,最好首先检查文件中是否有记录。下面的例子只有在文件中至少有一个记录时才查询brown级别记录。使用AND复合语句实现这一功能。意即至少存在一个记录后,查询字符串brown,最后打印结果。
# awk '{if (NR>0 && $4~/Brown/)print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99   4712 Brown-2 12 30 28
NF的一个强大功能是将变量$PWD的返回值传入awk并显示其目录。这里需要指定域分隔符/。
# echo $PWD | awk -F/ ' {print $NF}'
sam
另一个例子是显示文件名。
# echo "/usr/local/etc/rc.sybase" | awk -F/ '{print $NF}'
rc.sybase
如果不指定域分割符,返回的如下:
# echo $PWD | awk '{print $NF}'
/usr/sam
# echo "/usr/local/etc/rc.sybase" | awk '{print $NF}'
/usr/local/etc/rc.sybase
awk的其他操作
1. 设置输入域到域变量名
在awk中,设置有意义的域名是一种好习惯,在进行模式匹配或关系操作时更容易理解。一般的变量名设置方式为name=$n,这里name为调用的域变量名, n为实际域号。例如设置学生域名为name,级别域名为belt,操作为name=$1;belt s=$4。注意分号的使用,它分隔awk命令。
下面例子中,重新赋值学生名域为name,级别域为belts。查询级别为Yellow的记录,并最终打印名称和级别。
$awk '{name=$1;belts=$4;if(belts ~/Yellow/) print name" is belt "belts}' grade.txt
P.Bunny is belt Yellow
2. 域值比较操作
有两种方式测试一数值域是否小于另一数值域。
1) 在BEGIN中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在BEGIN部分赋值是很有益的,可以在awk表达式进行改动时减少很多麻烦。
使用关系操作必须用圆括号括起来。
下面的例子查询所有比赛中得分在27点以下的学生。用引号将数字引用起来是可选的,"27"、27产生同样的结果。
$awk '{if ($6<"27") print $0}' grade.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
第二个例子中给数字赋以变量名BASELINE和在BEGIN部分给变量赋值,两者意义相同。
$awk 'BEGIN{BASELINE="27"} {if ($6<BASELINE) print $0}' grade.txt
J.Lulu 06/99 48317 green 9 24 26
J.Troll 07/99 4842 Brown-3 12 26 26
3. 修改数值域取值
当在awk中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的awk复本。awk会在变量NR或NF变量中反映出修改痕迹。
为修改数值域,简单的给域标识重赋新值,如:$1=$1+5,会将域1数值加5,但要确保赋值域其子集为数值型。
修改M.Tansley的目前级别分域,使其数值从40减为39,使用赋值语句$6=$6-1,当然在实施修改前首先要匹配域名。
$awk '{$6=$6-1;print $1,$6,$7}' grade.txt
M.Tans 39 44
J.Lulu 24 26
P.Bunny 35 28
J.Troll 26 26
L.Tansl 30 28
$awk '{if($1=="M.Tans") {$6=$6-1;print $1,$6,$7}}' grade.txt
M.Tans 39 44
4. 修改文本域
修改文本域即对其重新赋值。需要做的就是赋给一个新的字符串。在J.Troll中加入字母,使其成为J.L.Troll,表达式为$1="J.L.Troll",记住字符串要使用双引号,并用圆括号括起整个语法。
$awk '{if($1=="J.Troll") $1="J.L.Troll"; print $1}' grade.txt
M.Tans
J.Lulu
P.Bunny
J.L.Troll
L.Tansl
5. 只显示修改记录
上述例子均是对一个小文件的域进行修改,因此打印出所有记录查看修改部分不成问题,但如果文件很大,记录甚至超过1 0 0,打印所有记录只为查看修改部分显然不合情理。在模式后面使用花括号将只打印修改部分。取得模式,再根据模式结果实施操作,可能有些抽象,现举一例,只打印修改部分。注意花括号的位置。
$awk '{if($1=="J.Troll") {$1="J.L.Troll";print $1}}' grade.txt  
J.L.Troll
6. 创建新的输出域
在awk中处理数据时,基于各域进行计算时创建新域是一种好习惯。创建新域要通过其他域赋予新域标识符。如创建一个基于其他域的加法新域{$4=$2+$3},这里假定记录包含3个域,则域4为新建域,保存域2和域3相加结果。
在文件grade.txt中创建新域8保存域目前级别分与域最高级别分的减法值。表达式为'{$8=$7-$6}',语法首先测试域目前级别分小于域最高级别分。新域因此只打印其值大于零的学生名称及其新域值。在BEGIN部分加入tab键以对齐报告头。
$awk 'BEGIN{print "Name\tDifference"}{if($6<$7) {$8=$7-$6;print $1,$8}}' grade.txt
Name    Difference
M.Tans 4
J.Lulu 2
当然可以创建新域,并赋给其更有意义的变量名。例如:
$awk 'BEGIN{print "Name\tDifference"}{if($6<$7) {diff=$7-$6;print $1,diff}}' grade.txt
Name    Difference
M.Tans 4
J.Lulu 2
7. 增加列值
为增加列数或进行运行结果统计,使用符号+=。增加的结果赋给符号左边变量值,增加到变量的域在符号右边。例如将$1加入变量total,表达式为total+=$1。列值增加很有用。许多文件都要求统计总数,但输出其统计结果十分繁琐。在awk中这很简单,请看下面的例子。
将所有学生的'目前级别分'加在一起,方法是tot+=$6,tot即为awk浏览的整个文件的域6结果总和。所有记录读完后,在END部分加入一些提示信息及域6总和。不必在awk中显示说明打印所有记录,每一个操作匹配时,这是缺省动作。
$awk '(tot+=$6); END{print "Club student total points :" tot}' 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
如果文件很大,你只想打印结果部分而不是所有记录,在语句的外面加上圆括号()即可。
$awk '{(tot+=$6)}; END{print "Club student total points :" tot}' grade.txt
Club student total points :155
8. 文件长度相加
在目录中查看文件时,如果想快速查看所有文件的长度及其总和,但要排除子目录,使用ls -l命令,然后管道输出到awk,awk首先剔除首字符为d(使用正则表达式)的记录,然后将文件长度列相加,并输出每一文件长度及在END部分输出所有文件的长度。
本例中,首先用ls -l命令查看一下文件属性。注意第二个文件属性首字符为d,说明它是一个目录,文件长度是第5列,文件名是第9列。如果系统不是这样排列文件名及其长度,应适时加以改变。
下面的正则表达式表明必须匹配行首,并排除字符d,表达式为^[^d]。使用此模式打印文件名及其长度,然后将各长度相加放入变量tot中。
$ls -l | awk '/^[^d]/ {print $9"\t"$5} {tot+=$5} END {print "total KB:" tot}'
...................
total KB:174144

实例演示

$ cat employees2
Tom Jones:4432:5/12/66:543354
Marry Adams:5436:11/4/63:28765
Sally Chang:1655:7/22/54:650000
Billy Black:1683:9/23/44:336500
打印出以“:”为域分隔符中第4域小于30000的记录

 

$ awk -F: '{if($4<300000) print $0}' employee2 
Marry Adams:5436:11/4/63:28765

 

打印出以“:”为域分隔符中第4域小于30000的记录中的第1域和第二域

 

$ awk -F: '{if($4<300000) print $1,$2}' employee2 
Marry Adams 5436

打印出以“:”为域分隔符中第4域大于350000 且小于 550000的记录
$ awk -F: '{if(350000<$4 && $4<550000) print $0}' employee2 
Tom Jones:4432:5/12/66:543354
打印出以“:”为域分隔符中第2域和第4域之和的平均值小于270000的记录
$ awk -F: '{if(($2+$4)/2<270000) print $0}' employee2 
Marry Adams:5436:11/4/63:28765
Billy Black:1683:9/23/44:336500
应用wage这个自定义变量,打印出以“:”为域分隔符且第一域包含“Tom"关键字的第2域和第4域之和
$ awk -F: '{wage=$2+$4}{if($1~/Tom/) print wage}' employee2 
547786
找出以“:”为域分隔符且第二域等于4432的记录,并把第2域赋值为1111进行打印
[czmmiao@czmmiao txt]$  awk -F: '$2 == 4432 { $2 = 1111;print}' employees2
Tom Jones 1111 5/12/66 543354
找出以“:”为域分隔符且第一域等于Sally Chang的记录,打印出记录行号,第1域,第2域和最后一个域。这里注意,如果不加“ ”那么,awk会匹配空字符串结果如下
$ awk -F: '{if($1=="Sally Chang") print NR,$1,$2,NF}' employee2 
3 Sally Chang 1655 4
设定以“:”为域分隔符,制表符(\t)为,两个回车为记录分隔符,打印第1域,第2域,第3域
$ awk 'BEGIN{FS=":";OFS="\t";ORS="\n\n"}{print $1,$2,$3}' employee2 
Tom Jones       4432    5/12/66

Marry Adams     5436    11/4/63

Sally Chang     1655    7/22/54

Billy Black     1683    9/23/44

找出以“:”为域分隔符,打印记录中包含"sally chang"的记录号,第1域,第2域和最后一域。其中IGNORECASE=1参数表示忽略大小写,这样"sally chang" 能够匹配"Sally Chang"
$ awk -F: 'BEGIN{IGNORECASE=1}{if($1=="Sally Chang") print NR,$1,$2,$NF}' employee2 
3 Sally Chang 1655 650000
打印“MAKE YEAR"跟在BEGIN后的语句先执行,即使没有输入文件,awk仍然执行BEGIN后的语句
$ awk 'BEGIN{print "MAKE YEAR"}'
MAKE YEAR
打印“The number of records is "记录号,跟在END后的语句最后执行。(下例体现得更明显。。。。。)
$ awk 'END{print "The number of records is "NR}' employees2
The number of records is 4
查找包含Marry的字段,每查找到一个自定义变量+1最后执行打印“Marry was found",变量count, "times."
$ awk '/Marry/{count++}END{print "Marry was found " count " times."}' employees2
Marry was found 1 times.

 

参考至:《UNIX® Shells by Example Fourth Edition》By Ellie Quigley 
                  《LINUX与UNIX SHELL编程指南》
                 http://blog.sina.com.cn/s/blog_45b28bfb0100o0fs.html

本文为原创文章,转载请注明出处、作者

如有错误,欢迎指正

邮箱: czmcj@163.com

作者:czmmiao 原文地址:http://czmmiao.iteye.com/blog/939160
相关文章
|
7月前
|
Shell Linux C++
Linux C/C++ 开发(学习笔记二):Shell脚本编程案例
Linux C/C++ 开发(学习笔记二):Shell脚本编程案例
120 0
|
运维 Shell C语言
运维(20)- shell awk
运维(20)- shell awk
60 0
|
Shell 数据处理 Perl
shell脚本里的三剑客之一awk
shell脚本里的三剑客之一awk
91 2
|
3月前
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
3月前
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
3月前
|
存储 Java Shell
shell学习笔记(详细整理)
这篇文章是一份详细的Shell学习笔记,涵盖了Shell的基础知识、脚本编写、变量、运算符、条件判断、流程控制、函数以及常用Shell工具的使用。
69 1
|
5月前
|
Shell Linux Perl
shell 编程中 awk ,wc ,$0,$1 等 命令的使用总结
shell 编程中 awk ,wc ,$0,$1 等 命令的使用总结
158 0
|
7月前
|
Shell Linux Perl
Linux|如何允许 awk 使用 Shell 变量
Linux|如何允许 awk 使用 Shell 变量
100 2
|
7月前
|
Shell Perl
shell学习(十) 【shell awk基本语法】
shell学习(十) 【shell awk基本语法】
40 1
|
7月前
|
存储 Shell 索引
第七章 Shell文本处理三剑客之awk
第七章 Shell文本处理三剑客之awk