AWK高级编程
来源:互联网 发布:龙门镖局源码 编辑:程序博客网 时间:2024/05/29 10:57
1. 程序元素
一个awk程序是一对以模式(pattern)与大括号框起来的操作(action)组合而成的,或许,还会加上实现操作细节的函数(function )。针对每个匹配于输人数据的模式,操作会被执行,且所有模式都会针对每条输人记录而检查。模式或操作可省略其中一个。如果模式省略,则操作将被应用到每条输人记录;如果操作省略,则默认操作为打印匹配之记录在标准输出上。以下是传统awk程序的配置:
pattern {action} 如模式匹配,则执行操作
pattern {action} 如模式匹配,则打印记录
虽然,模式多半是数字或字符串表达式,不过awk以保留字BEGIN与END提供两种特殊模式。
与BEG工N关联的操作只会执行一次,在任何命令行文件或一般命令行赋值被处理之前,但是在任何开头的一V选项指定已经完成之后。
END操作也是只执行一次,用于所有输入数据已被处理完之后。它多半用于产生摘要报告,或是执行清除操作。
BEGIN与END模式可以是任意顺序,可以存在于awk程序内的任何位置。不过,为了方便,我们通常将BEGIN模式放在程序的第一个位置,而将END模式放在最后。
2.注释与空白
awk里的注释是从#开始到该行结束,就像在Shell里那样。空行等同于空的注释。
3.字符串与字符串表达式
awk字符串包含零至多个字符,且在字符串的长度上没有限制,视可用内存而定。
字符串的比较,用的是传统的关系运算符:==(相等)、!=(不等)、<(小于)、<=(小于等于)、>(大于),以及>=(大于等于》。比较后返回l为真,0为假。比较不同长度的字符串,且其中一个字符串为另一个的初始子字符串时,较短的会定义为小于较长的那个,因此,“A”<“AA”的值为真。
awk并无特殊的字符串接续运算符。也就是说,两个连续字符串,会自动地连接在一起。以下每一组赋值设置标量变量。为相同的具有四个字符的字符串:
s =“ABCD”
s =“AB”“CD”
s =“A”“B”“CD”
s =“A”“B”“C”“D”
字符串不需要是常数,如果我们继续上述的赋值:
t= s s s
则t的值为“ABCDABCDABCD“。.
将数字转换为字符串,通过数字连接空字符串即可
n =123,
接着是:
s = ““ n,把值“123”赋给s。
awk功能强大的地方大多来自于它对正则表达式的支持。有两个运算符:~(匹配)与!~(不匹配)让awk更容易使用正则表达式:”ABC”~”^[A-Z]+$“,结果为真。
4. 数值与数值表达式
所有awk里的数字,都以双精确度的浮点值表示。浮点数可以包含一个末端以字母e(或E)所表示的10次方指数以及可选地带正负号的一个整数。举例来说:0.03125, 3.125e-2, 3125e-5与0.003125E1,同样都是表示1/32。因为awk里所有算术都是浮点算术。
awk并没有提供字符串转数字的函数,不过awk的做法很简单:只要加个零到字符串里,例如:s="123",接着是n=0+s,便将数字123赋值给n了。
5. awk的数值运算符
表9一:awk的数值运算符(优先级由大到小排列)
运算符 说明
++ -- 增加与减少(前置或后置)
^ ** 指数(右结合性)
! + - 非、一元(unary)加号、一元减号
* / % 乘、除、余数
+ - 加、减
< <= == !=> >= 比较
&& 逻辑AND(简写)
|| 逻辑OR(简写)
?: 三元条件式
= += -= *= /= %= ^=**= 赋值(右结合性)
6. 标量变量
保存单一值的变量叫做标量变量。
awk的变量名称必须以ACSII字母或下划线开始,然后选择性地接上字母、下划线及字。因此,变量名称要匹配正则表达式[A-Za-z-][A-Za-z_0-9]*。变量名称在实际上并没有长度的限制。awk的变量名称是与大小写有关的:foo, Fo。与FOO是完全不同的三个名称。一般使用上以及建议用法是:养成习惯,将局部变量全设为小写、全局变量第一个字母为大写,而内建变量则全是大写。
7. 数组变量
awk允许在数组名称之后,以方括号将任意数字或字符串表达式括起来作为索引。例如:
telephone["Alice"]=“555-0134"
telephone["Bob"]=“555-0135"
telephone["Carol”]=“555-0136"
telephone["Don"]=“555-0141"
以任意值为索引的数组,称之为关联数组,因为它们的名称与值是相关联的。重要的是,awk将其应用于数组中,允许查找( find )、插入(insert )以及删除( remove)等操作,在一定的时间内完成,与存储多少项目无关。
一个变量不能同时用作标量变量和数组变量。当你应用delet。语句删除数组的元素
(element]的时候,不会删除它的名称。因此。像这样的代码:
x[1]=3
delete x
x=789
会引发awk发出提示,告诉你不可以给数组名称赋值.
8. 命令行参数
awk通过内建变量ARGC(参数计数)与ARGV(参数向量,或参数值),让命令行参数
可用。下面简短的程序说明其用法;
[root@local~]#cat showargs.awk
BEGIN{
print ”ARGC=”,ARGC
for (k=0;k<ARGC; k++)
print "ARGV[”k”]=[”ARGV[k]“
}
再来看看将它用在一般awk命令行上,会产生什么样的结果:
[root@local~]# awk-v One=1 -v Two=2 -f showargs.awkThree=3 file1 Four=4 filet2 file3
ARGC=6
ARGV[0]=[awk]
ARGV[1]=[Three=3]
ARGV[2]=[file1]
ARGV[3]=[Four=4]
ARGV[4]=[file2]
ARGV[5]=[file3]
9. 环境变量
awk提供访问内建数组ENV工RON中所有的环境变量:
[root@local~]#awk'BEGIN {print ENVIRON["HOME"];print ENVIRON["USER]}‘
/home/Jones
hones
通常你应将ENVIRON看成是一个只读数组。
10. 模式
模式由字符串与/或数值表达式构建而成。常用的模式如下:
NF==0 选定空记录
NF>3 选定拥有三个字段以上的记录
NR<5 选定第1到第4条记录
(FNR==3)&&(FILENAME~/[.] [ch]$/) 来源于C源文件中选定记录3
$1~/Jones/ 选定字段1里有.”jones“的记录
/[Xx][Mm][Ll]/ 选定含有‘'XML'。的记录,并忽略大小写差异
$0~/[Xx][Mm][Ll]/ 同上
11. 操作
以最简单的形式来说,纯print意指在标准输出上,打印当前的输入记录($0),接着是输出记录分隔字符)ORS的值,默认为单一换行字符。因此,下面这些程序所做的全是相同的操作:
1 模式为真,默认操作为打印
NR>0 {print} 有记录时打印(恒为真)
1 {print} 模式为真。则打印,这是默认值
{print} 无模式则视为真,明确的打印,这是默认值
{print $0} 相同,但打印明确的值
下面的例子已经是完整的awk程序。在每一个中,我们都只显示前三个输入字段,并通过省略选定模式,选定所有的记录。awk程序语句以分号分隔,而且我们会使用些略微不同的操作代码,以修改输出字段分隔字符:
[root@local~]#echo ‘onetwo three four'| awk ‘{print$1,$2,$3}’
one two three
[root@local~]#echo ‘onetwo three four'| awk ‘{OFS=”…”;print$1,$2,$3}’
one…two…three
[root@local~]#echo ‘onetwo three four'| awk ‘{OFS=”/n”;print$1,$2,$3}’
one
two
three
改变输出字段分隔字符而没有指定任何字段,不会改变$0:
[root@local~]#echo ‘onetwo three four'| awk ‘{OFS=”/n”;print$0}’
one two three four
不过,如果我们更改输出字段分隔字符,并指定至少一个字段(即使我们未变更其值),
强制以新的字段分隔字符重新组合记录,则结果为:
[root@local~]#echo ‘onetwo three four'| awk ‘{OFS=”/n”;$1=$1;print$0}’
one
two
three
four
12. 在awk中的单行程序
1.UNIX单词计数程序wc;
[root@local~]#awk ‘{C+=length($0)+1;w+=NF} END {print NR,W, C}’
2. 撇开NUL字符问题,awk其实可以轻松取代cat,下面这两个例子会产生相同输出:
[root@local~]# cat*.xml
[root@local~]# awk1*.xml
3. 要将原始数据值及它们的对数打印为单栏的数据文件,可使用:
[root@local~]# awk ‘{print$1, log($1)}’file(s)
4. 在以空白分隔字段的表格中,报告第n栏的和:
[root@local~]# awk-v COLUMN=n ‘{sum+=$COLUMN} END {print sum}’file (s)
5.微调上述报告,产生字段n的平均值:
[root@local~]# awk-v COLUMN=n ‘{sum+=$COLUMN} END {print sum/NR }’file (s)
6.针对花费文件(其记录包含描述与金额于最后一个字段),打印花费总数。可使用内建变量NF计算总值:
[root@local~]# awk’{sum+=$NF;print $0, sum}’files)
7.这里是三种查找文件内文本的方式:
[root@local~]#egrep‘pattern|pattern’ file (s)
[root@local~]#awk ‘/pattern|pattern/’file(s)
[root@local~]#awk ‘/pattern}pattern/{print FILENAME ”:”FNR”:”$0}’file(s)
8. 如果你要限制仅查找100一150行,可以通过两个工具程序,再搭配管道,不过这么做会漏掉位置信息:
[root@local~]#sed -n-e 100,150p -s file(s) | egrep 'pattern'
使用GNU sed要搭配-s选项,才能为每个文件重新开始行编号。另外,你也可以通过awk,使用比较花哨的模式来做:
[root@local~]#awk ‘(100<=FNR)&& (FNR <= 150) && /pattern//
{print FILENAME ”:” FNR ”:”$0}’file(s)
9. 要在一个四栏表格里,调换第二与第三栏,假设它们是以制表字符分隔,那么可以
使用下面三种方式的其中一种:
[root@local~]#awk-F'/t’-v OFS='/t’{print $1, $3, $2, $4}’old > new
[root@local~]#awk ‘BEGIN{FS=OFS="/t"}{print$1, $3,$2,$4}’old>new
[root@local~]#awk –F‘/t’{print $1"/t"$3"/t" $2"/t" $4}’old>new
10.要将各栏分隔字符由制表字符(在此以·显示)转换成&,可在以下两种方式择一:
[root@local~]#sed -e's/·/&/g' file(s)
[root@local~]#awk ‘{BEGIN{FS="/t";OFS=“&”}{$1=$1;print}’file(s)
11.下面这两个管道,都为删除已排序流里的重复行
[root@local~]#sortfile(s)|uniq
[root@local~]# sortfile(s)|awk ‘Last!=$0 { print}{Last=$0}’
12. 将回车字符/换行字符的行终结,一致转换为以换行字符作为行终结,可在下列方
式中选择一种:
[root@local~]#sed –e‘s//r$//’ file(s)
[root@local~]#sed –e‘s/^M$//’ file(s)
[root@local~]# mawk ‘BEGIN{RS=“/r/n"} {print}’file(s)
13. 要将单空格的文本行,转换为双空格的行,可在下列方式选择一种
[root@local~]#sed –e‘/s/$//n/’file(s)
[root@local~]#awk ‘BEGTN{ORS = "/n/n"){print}’file(s)
[root@local~]#awk ‘BEGIN{ORS="/n/n" }1 ’ file(s)
[root@local~]#awk {print$0 “/n”}’ file(s)
[root@local~]#awk ‘{print;print” ”}’ file(s)
13. 语句
13.1. 条件语句
if(expressionl)
stateme刀t1
else if(expression2)
statement2
else if(expression3)
statement3
else if(expressionk)
statementk
else
statementk+l
13.2. 重复执行
awk提供了4种重复执行语句(循环):
1.循环在起始处使用结束测试:
while(expression)
statement
2.循环在结尾处使用结束测试:
do
statement
while (expression)
3.循环执行可计数的次数:
for(expr1;expr2; expr3)
statement
4.循环处理关联数组里的元素:
for(key in array)
statement
例如:
for (name in telephone)
print name“/t" telephone[name]
13.3 数组成员测试
成员测试key inarray是一个表达式:如果key为array的一个索引元素,则计算为1(真)。如果key不是array的一个索引元素,则!(keyin array)为1。
对于具有多下标(subscript)的数组,在测试时,请使用圆括号,并以逗点分隔下标列表:(i,j,…,n)in array
成员测试不可能建立数组元素,然而引用元素时,如果元素不存在,便会建立它。因此你应该这么写:
if("Sally"in telephone)
print "Sally is in the directory"
而非:
if(telephone["Sally"]!=””)
print "Sally is in the directory"
因为第二种形式会在她(Sally)不存在时,将其加入到目录里,并拥有一个空电话号码。
重点是:你必须能够区分寻找索引(index)与寻找特定值(value)的差异。索引成员测试需要固定的时间,而值的查找时间是与数组里元素的个数成正比,这点我们在先前已通过break语句内的for循环解释过了。如果你需要时常用到这两种运算,那么构建反索引数组会比较实用:
for (name intelephone)
name_by_telephone[telephone[name]]=name
接下来,你就可以使用name_by_telephone["555-0136"]在一定时间内找到”Carol"。当然,这里假定所有的值是唯一的:如果这两人共享同一个电话,则name_by_telephone数组只会记录最后一个名称。只要稍做修改就能解决这个问题:
for (name intelephone)
{
if (telephone[name] in name_by_telephone)
name_by_telephone[telephone[name]]=/
name_by_telephone[telephone[name]) “/t”name
e1se
name_by_telephone[telephone[name]]=name
现在,name_by_telephone即包含了以制表字符分隔的具有相同电话号码的人名列表。
14. 用户控制输入
awk也可以通过的getline语句做这件事。getline会返回一个值,当输入被成功读取时,它的返回值为++I,而返回值为0时,则表示在文件结尾,而-1则表示错误。它的用法很多,见表。
语法 说明
getline 从当前输入文件中,读取下一条记录,存入$0,并更新NF, NR与FNR
getline var 从当前输入文件中,读取下一条记录,存入var,并更新NR与FNR
getline<file 从file文件中,读取下一条记录,存入$0,并更新NF, NR与FNR
getline var<file从file文件中,读取下一条记录,存入var,并更新NF, NR与FNR
cmd|getline 从外部命令cmd读取下一条记录,存入$0,并更新NF
cmd|getline var从外部命令cmd读取下一条记录,存入var
命令管道在awk里可以发挥强大的功能。管道可以在字符字符串中标明,也可以包含任
意的Shell命令。这里是与getline搭配使用,如下:
"date" I getline now
close("date")
print "The current time is".now
接下来说明的是:如何在循环里使用命令管道:
command="head -n 15 /etc/hosts"
while((command I getline s)>0)
print s
close(command)
15. 执行外部程序
这里是解决电话名录排序问题较短的程序方案,使用临时性文件与systemty,而非awk管道:
tmpfile=“/tmp/telephone.tmp^
for (name intelephone>
print name "/t"telephone[name]>tmpfile
close(tmpfilej
system("sort <tmpfile“)
临时性文件必须在调用system()之前关闭,以确保任何缓冲区输出都正确地记录在文件内。
对于被system()执行的命令并不需要调用close(),因为close()仅针对以I/O重定向运算符所打开的文件或管道,还有getline, print或printf。
传递给system[f的命令可包含数行
system("cat <<EOFILE/nuno/ndos/ntres/nEOFILE"
它产生的输出和从嵌入文件复制到标准输出一样
Un0
das
tres
16. 用户自定义函数
函数定义如下:
function name(argl, }rg2,…,argn
{
statements
}
指定的参数在函数体中用来当作局部变量,它们会隐藏任何相同名称的全局性变量。函数也可用于程序它处,调用的形式为:
name(exprl, expr2,…,expn) 忽略任何的返回值
result=name(exprl, expr2, …,exprn) 将返回值存储到result中
在每个调用点上的表达式,都提供初始值给函数参数型变量。以圆括号框起来的参数,必须紧接于函数名称之后,中间没有任何空白。
对标量参数所做的变动,调用者无从得知,不过对数组的变动就可看见了。换句话说,标量为传值(by vaule ),而数组则为传引用(by reference):这对C语言也是这样。
函数体里的return expression语句会终止主体的执行,并将expression的值与控制权传给调用点。如果expression省略,则返回值由实现期定义。我们测试过的所有系统,返回的不是数字零就是空字符串。
17. 字符串函数
17.1. 子字符串提取
提取子字符串的函数:substr(string,start, 1en),会返回一份由string的start字符开始,共len个字符长度的子字符串副本。字符的位置,从1开始编号:substr("abcde", 2, 3)将返回。bcd"。 len参数可省略,省略时,则默认为length(string)-start+1,选出字符串的剩余部分。
17.2. 字符串大小写转换
tolower(string)会返回将所有字母改为同义的小写的string副本,而toupper(string)则返回被改为大写字母的string副本。所以tolower("aBcDeF123")返回”abcdef123",toupper("aBcDeF123")返回"ABCDEF123"。
17.3. 字符串大小写转换
index(string, find)查找string里是否有字符串find,然后返回string里find字符串的起始位置,如果在string里找不到find,则返回0。例如index("abcdef","de")会返回4。
17.4. 字符串匹配
match(string, regexp)将string与正则表达式regexp匹配,如果匹配,则返回
匹配string的索引,不匹配,则返回0。这种方式提供了比表达式(string~regexp)还多的信息,后者只能得到计算值1或0。另外match ( )也具有一个有用的副作用:它会将全局变量RSTART设为在string中要开始匹配的索引值,而将RLENGTH设为要匹配的长度。而匹配子字符串则以substr(string,RSTART, RLENGTH)表示。
17.5. 字符串替换
awk在字符串替换功能上,提供两个函数:sub(regexp,replacement, target)与gsub(regexp, replacement, target),sub()将target与正则表达式regexp进行匹配,将最左边最长的匹配部分替换为字符串replacement。gsub()的运行则有点类似,不过它会替换所有匹配的字符串(前置g表示global全局之意)。
17.6. 字符串替换
awk针对当前输人记录$0自动提供了方便的分割为字1, $},…、$NF,也可以函数来做:split(string, array, regexp)将string切割为片段,并存储到array里的连续元素。在数组里,片段放置在匹配正则表达式regexp的子字符串之间。如果regexp省略,则使用内建字段分隔字符FS的当前默认值。函数会返回array里的元素数量。
17.7. 字符串重建
join()可确保参数数组不会被引用到,除非索引是在范围之内。否则,一个具有数组长度为0的调用可能会建立arrayfl3,而修改了调用者的数组。插人的字段分隔字符为普通字符串,而非正则表达式,所以针对传递给split()的一般正则表达式,join()不会重建精确的原始字符串。
17.8. 字符串格式化
最后一个与字符串相关的函数是在用户控制下格式化数字与字符串:sprintf (format,expression1, expression2,…),它会返回已格式化的字符串作为其函数值。printf()的运行方式也是这样,只不过它会在标准输出或重定向的文件上显示格式化后的字符串,而不是返回其函数值。较新的程序语言以更强大的格式化函数来取代格式控制字符串,但相对而言让代码变得很冗长。按照传统的文本处理应用来说,sprintf与printf
18. 数值函数
函数 说明
atan2(y, x) y返回y/x的反正切,值介于-pai与+pai之间。
cos(x) 返回x的余弦值(以弧度(radians)计算),该值介于-1与+1之间
exp(x) 返回x的指数,ex,
int(x) 返回x的整数部分,截去前置的0
log(x) 返回x的自然对数。
rand() 返回平均分布的虚拟随机r,O<=r<l
sin(x) 返回x的正弦值(以弧度(radians]计算),该值介于-1与+1之间
sqrt(x) 返回x的平方
srand(x) 设置虚拟随机产生器的种子为x,并返回正确的种子。如果省略x,则使用当前时间(以秒计)。如果。rand ( )未被调用,则awk在每次执行时会从相同的默认种子开始;mawk则不会。
- AWK高级编程
- AWk高级编程
- awk高级编程
- Shell编程笔记(高级1)-深入讨论awk和<<
- awk编程
- awk编程
- awk 编程
- awk编程
- awk编程
- awk 编程
- AWK编程
- awk编程
- awk 编程
- awk编程
- AWK高级应用
- awk数组高级
- 高级过滤器awk
- awk的高级用法
- 怎样成为一个黑客??如何计划??/
- httpd: bad user name apache
- JS金额小写转大写
- 晚睡强迫症..其实真的有——医学上的真正名字叫"拖延症"
- 软件无线电
- AWK高级编程
- 软件开发随记之二 —— 软件界面的Web化
- 我学习的黑客教程
- The error indicates that IIS is in 64 bit mode, while this application is a 32 bit application and thus not compatible
- 网络日志(2010.4.21)
- C#基础
- Apple 的CEO 竟然这样评论 Andriod!
- Spring 中常用的hql查询方法(getHibernateTemplate())(转)
- 原来抽象工厂比简单工厂就是多了一个抽象。