1,awk 处理流程
awk的处理流程分三部分,在没有读取任何数据前先执行BEGIN部分,然后进入主循环,
在主循环中读一行并进行处理,处理时先匹配模式部分,若模式部分不匹配将处理下一行。
当处理完所有输入行后,执行END的内容。
若没有BEGIN或END,直接进入主循环执行。
------------------------------------ ----------------
(1) 在任何数据读入前执行 BEGIN
------------------------------------ ----------------
(2) 主循环,完成所有的动作 read a line--> deal with the line
read and deal it
------------------------------------ ----------------
(3) 文件内容读取完毕后执行 END
------------------------------------ ----------------
格式形如:
awk 'BEGIN{print"start.......\n"} {print $1} END{print "end.....\n"}'
说明:例子文件data内容:
[root@myserver ~]# cat data
M.Ta 5/99 48011Green 4 40 44
N.Tans 5/99 58211 Green 840 41
S.Tns 5/99 38311 Green 5 4942
I.Tan 5/99 18321 Green 8 5040
L.Tas 5/99 28311 Green 6 4044
J.Lulu 06/99 48317 green 924 26
P.Bunny 02/99 48 Yellow 1235 28
J.Troll 07/99 4842 Brown-312 26 26
*控制流
if (condition)statement [ else statement ]
while (condition) statement
do statement while(condition)
for (expr1; expr2; expr3)statement
for (var in array)statement
break
continue
delete array[index]
delete array
exit [ expression ]
{ statements }
2, awk 支持的正则表达式
c matches the non-metacharacter c.
\c matches the literal character c.
. 匹配任何一个字符,包括换行符
^ 匹配从字符串的开始位置
$ 匹配到字符串的尾部
[abc...] 字符列表,匹配[]中的任何一个字符
[^abc...] 非[]列表中的任何字符
r1|r2 可选正则匹配,匹配正则表达式1或表达式2
r1r2 匹配r1和r2都符合的行
r+ 匹配符合正则表达式r一次或多次的行
r* 匹配0次或多次r的行
r? 匹配0次或一次正则表达式r的行。
(r) grouping: matches r.
r{n}
r{n,}
r{n,m} One ortwo numbers inside braces denote an interval expression. If there is onenumber in the braces,
the preceding regular expression r isrepeated n times. If there are two numbers separated by a
comma, r is repeated n to m times. If there is one number followed by a comma, then r is repeated at
least n times.
Interval expressions are only available ifeither --posix or --re-interval is specified on the command
line.
3,awk 中的模式匹配
*a w k条件操作符
操作符描述操作符描述
< 小于
> = 大于等于
< = 小于等于
~ 匹配正则表达式
!~ 不匹配正则表达式
= = 等于
!= 不等于
*正则表达式匹配 ~
$1 ~ /regexp/ ~后面接正则表达式,表达式要用/正则表达式/括起来。
在使用正则表达式时,可以有两种形式,使用if或不适用if:
awk '$n~/regexp/ {dosomthing}' datafile
或
awk '{if($n~/regexp/) do somthing}' datafile
例如:
找出第3列中以48开头的行
[root@myserver ~]# awk'$3~/^48/ {print $0}' data
M.Ta 5/99 48011Green 4 40 44
J.Lulu 06/99 48317 green 924 26
P.Bunny 02/99 48 Yellow 1235 28
J.Troll 07/99 4842 Brown-312 26 26
[root@myserver ~]# awk'{if($3~/^48/) print $0}' data
M.Ta 5/99 48011Green 4 40 44
J.Lulu 06/99 48317 green 924 26
P.Bunny 02/99 48 Yellow 1235 28
J.Troll 07/99 4842 Brown-312 26 26
*精确匹配 ==
[root@myserver ~]# awk'$3==48{print $0}' data
P.Bunny 02/99 48 Yellow 1235 28
[root@myserver ~]# awk'{if($3=="48") print $0}' data
P.Bunny 02/99 48 Yellow 1235 28
*不匹配 !~
找出在第3列中不含11字符串的行
[root@myserver ~]# awk'$3!~/11/{print $0}' data
I.Tan 5/99 18321 Green 8 5040
J.Lulu 06/99 48317 green 924 26
P.Bunny 02/99 48 Yellow 1235 28
J.Troll 07/99 4842 Brown-312 26 26
*不等于 !=
找出第3列中不等于48的行
[root@myserver ~]# awk'{if($3!="48") print $0}' data
M.Ta 5/99 48011Green 4 40 44
N.Tans 5/99 58211 Green 840 41
S.Tns 5/99 38311 Green 5 4942
I.Tan 5/99 18321 Green 8 5040
L.Tas 5/99 28311 Green 6 4044
J.Lulu 06/99 48317 green 924 26
J.Troll 07/99 4842 Brown-312 26 26
*大于 >
#找出第6列的值>40的行
[zxh@localhost dosh]$ awk'{if($6>40) print $0}' data
S.Tns 5/99 38311 Green 5 4942
I.Tan 5/99 18321 Green 8 5040
#或者
[zxh@localhost dosh]$ awk'$6>40{print $0}' data
S.Tns 5/99 38311 Green 5 4942
I.Tan 5/99 18321 Green 8 5040
*大小写匹配 ~[aA]
*任意单个字符匹配 ~/^...a/
#匹配(1)第1列中第4个字符为r的行 或 (2) 第4列首字母为Y或y的行
[zxh@myserver dosh]$ catdata | awk '{if($1~/^...r/ || $4 ~/^[Yy]/) print $0}'
P.Bunny 02/99 48 Yellow 1235 28
J.Troll 07/99 4842 Brown-312 26 26
*正则的或关系~/(reg1|reg2)/
#在用或关系时,注意要用()括起来
[zxh@localhost dosh]$ awk'{if($3~/(^38|^18)/) print $0}' data
S.Tns 5/99 38311 Green 5 4942
I.Tan 5/99 18321 Green 8 5040
#上面也是或关系使用的例子
*正则表达中的与关系 ~/reg1/&& ~/reg2/
awk '{if($1=="P.Bunny" && $4=="Yellow") print $0}'grade.txt
*多次匹配
#每次匹配成功时先把结果输出,在进行第2个正则表达式的匹配。
[zxh@localhost dosh]$ awk'$1~/^L/{print $1}; $3~/^28/{print $3}' data
L.Tas
28311
* 已28开头11结尾的匹配
awk '$3~/^28.*11$/ {print$0}' data
4, awk常用内置变量
FILENAME 因为a w k可以同时处理许多文件,因此如果访问了这个变量,将告之系统目前正在浏览的实际文件。
FS 用来在a w k中设置域分隔符,与命令行中-F选项功能相同。缺省情况下为空格。
FNR 当前输入文件的记录数
NR N record。表示当前输入的总记录数,若是多个文件,它是多个FNR的和。
NF N field。表示当前输入记录的域(列)数。
OFS output field seperator。输出域分割符。默认是空格。
ORS output record seperator。输入记录(行)分割符,默认是换行符。
ARGC 命令行参数个数
RS 输入的记录分割符,默认是换行符\n,支持|或&&的支持
注意:在使用时,这些变量不需要加$符号。只有在取这些变量对应的记录值时才需要加。
*NF,NR,FILENAME
#计算文件的行数
[zxh@localhost dosh]$ timeawk 'END{print NR}' data
8
说明:#它的性能比起wc -l要慢许多
[zxh@localhost dosh]$ timewc -l data1
4194944 data1
real 0m0.163s
user 0m0.120s
sys 0m0.041s
[zxh@localhost dosh]$ timeawk 'END{print NR}' data1
4194944
real 0m0.525s
user 0m0.485s
sys 0m0.039s
#打印当前目录(取最后一个域的值)
[zxh@localhost dosh]$ echo$PWD | awk -F/ '{print $NF}'
dosh
#取文件名
[zxh@localhost dosh]$ echo"/usr/local/sbin/gfmd" | awk -F/ '{print $NF}'
gfmd
#cut 也可以做到,但比较笨一些
[zxh@localhost dosh]$ echo"/usr/local/sbin/gfmd" | cut -d/ -f 5
gfmd
5, awk 的操作符
详细说明可以参看:man awk
(...) 组
$ 域引用。
++ -- 自增和自减,作为前缀和后缀都可以。
^ Exponentiation (** mayalso be used, and **= for the assignment operator).
+ - ! 一元操作符:加,减,非.
* / % 乘,除,取模。
+ - 加,减.
space 字符串连接.
< >
<= >=
!= == 关系操作符
~ !~ 正则表达式匹配操作符。.
in 数组成员关系操作符。
&& 逻辑与
|| 逻辑或.
?: 和C的三元操作符相同
= += -=
*= /= %= ^= 计算操作符.
*使用自定义变量
#注意在引用自定义变量时,不需要添加$符号
#把各个域变量设置成可识别的变量,例如若1列表示名字,那么可以这样做: name=$1
[zxh@localhost dosh]$ awk'{name=$1;score=$3; if($name ~/^L/) print name,score}' data
L.Tas 28311
*修改域值
# 注意一下记录,$NF的值都变成了30
[zxh@localhost dosh]$ awk'{score=$NF; if(score<30) $NF=30; print score,$0}' data
... ...
26 J.Lulu 06/99 48317 green9 24 30
28 P.Bunny 02/99 48 Yellow12 35 30
26 J.Troll 07/99 4842Brown-3 12 26 30
*创建新的输出域
#上面的一个例子已经增加了一列,增加一列很容易吧。其实增加列还有另外一种方式。
#以下脚本,我想把最后的三个域的值加起来,然后放到最后一列输出,注意在输出$0时自动把最后一列加进来了。
#若把NF+1改成NF+2,则会自动添加一个空格列,也就是NF+1这一列。
[zxh@localhost dosh]$ awk'{ad=NF+1; $ad=$NF+$(NF-1)+$(NF-2); print $0}' data
M.Ta 5/99 48011 Green 4 4044 88
N.Tans 5/99 58211 Green 840 41 89
S.Tns 5/99 38311 Green 5 4942 96
... ...
*统计列的值
#统计最后一列的总和
[zxh@localhost dosh]$ awk'{tot+=$NF} END{print "last field total:"tot}' data
last field total:291
*文件长度相加
#计算一个目录下,非目录文件的总大小
[root@localhost ~]# ls -l |awk '/^[^d]/{print $5;s+=$5}; END{print "totalsize="s/1024"k"}'
1165
147
35566
5195
13
total size=41.0996k
#计算当前目录非目录文件总大小
[root@localhostlibevbase-0.0.14]# find . -type f -exec ls -l '{}' \; | awk '/^[^d]/{tot+=$5};END{print "size="tot/1024/1024"M"}'
size=2.22593M
6, 使用awk中的函数
*内置字符串函数
gsub( r, s) 在整个$ 0中用s替代r
gsub( r, s , t) 在整个t中用s替代r
index(s , t) 返回s中字符串t的第一位置,若没有找到返回0
length(s) 返回s长度
match(s , r) 测试s是否包含匹配r的字符串
split(s , a , fs) 在fs上将s分成序列a
sprint(fmt , exp) 返回经f m t格式化后的e x p
sub(r, s ) 用$ 0中最左边最长的子串代替s
substr(s , p) 返回字符串s中从p开始的后缀部分
substr(s , p , n) 返回字符串s中从p开始长度为n的后缀部分
说明:详细使用说明,请man awk,搜索gsub等。
*字符串替换
注意:字符串替换要加双引号: gsub(/oldstring/,"newstring",$1)
#在整个记录中把383替换成888
[zxh@myserver dosh]$ catdata | awk 'gsub(/383/,888){print $0}'
S.Tns 5/99 88811 Green 5 4942
*在某一列中替换
#把第1列中的字符串Lulu替换成字符串hover,注意加双引号
[zxh@myserver dosh]$ catdata | awk 'gsub(/Lulu/,"hover",$1){print $0}'
J.hover 06/99 48317 green 924 26
*替换字符串第一次出现的位置 sub
# 使用s u b发现并替换模式的第一次出现位置。和gsub相似,但只替换第一次出现的字符。
# sub(r, s [, t]) r是正则表达式;s是是要替换的字符串。t是域号,若没有指定,则是$0
[zxh@myserver dosh]$ catdata | awk '$0~/^L/{sub(44,55);print $0}'
L.Tas 5/99 28311 Green 6 4055
*查找字符串
#查找第一列中包含字符串"Lulu"的行
[zxh@myserver dosh]$ catdata | awk 'index($1,"Lulu"){print $0}'
J.Lulu 06/99 48317 green 924 26
*分割字符串split
[zxh@myserver dosh]$ catdata | awk '$1~/J\.L/{split($0,arr," ")} END {print arr[3]}'
48317
#另外一个例子
[root@Linux_chenwy sam]#awk 'BEGIN {print split("123#456#789",myarray,"#")}'
*输出字符串长度length
[zxh@myserver dosh]$ catdata | awk '$0~/^L/{print length($1),$1}'
5 L.Tas
*字符串切割
# substr(s, i [, n])
# Returns the at mostn-character substring of s starting at i. If n is omitted, the rest of sis used
#打印第2个域,从"/"开始的字符串其余部分
[zxh@myserver dosh]$ catdata | awk '$0~/^L/{sub(44,55);print substr($2,index($2,"/"))}'
/99
7, 使用awk脚本文件
可以将a w k脚本写入一个文件再执行它。命令不必很长(尽管这是写入一个脚本文件的主
要原因),甚至可以接受一行命令。
使用awk脚本文件有以下优点:
(1)这样可以保存a wk命令,以使不必每次使用时都需要重新输入。
(2)使用文件的另一个好处是可以增加注释,以便于理解脚本的真正用途和功能。
*基本格式
第一行必须是: !/bin/awk -f 表示awk命令从文件中读取命令行。
*例1
[zxh@localhost dosh]$ catdo.awk
#!/usr/bin/awk -f
# myfirst.awk
#
#
BEGIN {
print "--------------start-----------"
}
{
if ($5>10)
print "<10 "$5
else
print ">10 "$5
}
END {
print "-------------end--------------"
}