awk 学习笔记:
------------
·模式:
开始-主输入循环-结束
·主输入循环:模式匹配-记录-字段
·变量表达的字段:
[root@localhost ~]# echo a b c|awk 'BEGIN {one=1;two=2} {print $(one + two)}'
c
·-f选项:
[root@localhost ~]# awk -f list.awk name.log
andy,3233
steve,77899
[root@localhost ~]# cat list.awk
BEGIN { FS = " " }
{print $1 "," $2}
[root@localhost ~]# cat name.log
andy 3233
steve 77899
·分割符:
两个连续的分隔符间的字段值为空串。
·多个分隔符:(使用正则表达式表示)
[root@localhost ~]# awk -F "[:,]" '{print $1,$3}' name.log
andy xx
steve yy
[root@localhost ~]# cat name.log
andy,3233:xx
steve,77899:yy
·awk中对于变量赋值,空格为字符串间的连接操作符。
·模式匹配+算术运算:
使用awk,对于空行进行计数的脚本:
[root@localhost ~]# awk -f null_row.awk name.log
2
[root@localhost ~]# cat name.log
andy,3233:xx
steve,77899:yy
end,0:z
[root@localhost ~]# cat null_row.awk
/^$/ {
++x
}
END {
print x
}
·算术运算:
求取平均值脚本:
[root@localhost ~]# awk -f avg_grade.awk grade.log
andy 79.75
lily 62.75
lucy 70
steve 75.75
ann 76
[root@localhost ~]# cat avg_grade.awk
{
total = $2 + $3 + $4 + $5
avg = total / 4
print $1,avg
}
[root@localhost ~]# cat grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
·系统变量
FS:输入字段分割符
OFS:输出字段分割符
RS:输入记录分隔符
ORS:输出记录分割符
NF:当前记录中字段的总数
NR:当前的记录数
FILENAME:当前输入处理的文件名
·横跨处理多行记录
[root@localhost ~]# awk -f multi_row.awk multi_row.log
andy steve 678-888
lucy lee 890-777
[root@localhost ~]# cat multi_row.log
andy steve
hangzhou
789 street
678-888
lucy lee
hangzhou
301 street
890-777
[root@localhost ~]# cat multi_row.awk
BEGIN {
FS = "\n"
RS = ""
}
{ print $1,$NF }
·关系操作符(主要用于模式匹配)
大于-等于-小于-匹配-不匹配:
等于的例子:
[root@localhost ~]# awk -F "[:,]" 'NF == 3 {print $1}' name.log
andy
steve
end
[root@localhost ~]# cat name.log
andy,3233:xx
steve,77899:yy
end,0:z
匹配的例子:
[root@localhost ~]# awk -F "[:,]" '$1 ~ /andy/ {print $1,$3}' name.log
andy xx
·逻辑(布尔)操作符
逻辑或-与-非
与的例子:
[root@localhost ~]# awk -F "[:,]" '$1 ~ /andy/ && $2 == 3233 {print $1,$2}' name.log
andy 3233
·格式化打印
print:打印字符串和字段,自动在句末加入回车;
printf:格式化输出,不会在句末自动加入回车,需要明确指定“\n”
[root@localhost ~]# awk '{printf "|%-10s|%3d\n",$1,$2}' grade.log
|andy | 86
|lily | 66
|lucy | 77
|steve | 82
|ann | 80
[root@localhost ~]# cat grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
·向awk脚本传递参数(变量one、two)
[root@localhost ~]# awk -f var_input.awk one=1 two=2 grade.log
87 80
67 72
78 80
83 80
81 80
[root@localhost ~]# cat grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
[root@localhost ~]# cat var_input.awk
{
$2 += one
$3 += two
print $2,$3
}
另外一种获取方法:(使用-v选项,POSIX awk支持)
[root@localhost ~]# awk -v one=1 -v two=2 -f var_input.awk grade.log
87 80
67 72
78 80
83 80
81 80
#####进阶部分#####
·条件语句(基本上合C语言语法类似,下同)
一个例子:计算平均分,并显示为等级:
[root@localhost ~]# awk -f gtoclass.awk grade.log
andy 79.75 C
lily 62.75 D
lucy 70.00 C
steve 75.75 C
ann 76.00 C
[root@localhost ~]# cat grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
[root@localhost ~]# cat gtoclass.awk
{
total = $2 + $3 + $4 + $5
avg = total / ( NF -1 )
if (avg >= 90) class = "A"
else if (avg >= 80) class = "B"
else if (avg >= 70) class = "C"
else if (avg >= 60) class = "D"
else class = "E"
printf "%-10s%5.2f%5s\n",$1,avg,class
}
·循环语句
for while do-while
求平均值的另外一种方法:
[root@localhost ~]# awk -f for_avg.awk grade.log
andy 79.75
lily 62.75
lucy 70
steve 75.75
ann 76
[root@localhost ~]# cat grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
[root@localhost ~]# cat for_avg.awk
{
total = 0
for ( i =2; i <= NF; ++i )
total += $i
avg = total / (NF - 1)
print $1,avg
}
使用while时的类似方法:
[root@localhost ~]# awk -f while_avg.awk grade.log
andy 79.75
lily 62.75
lucy 70
steve 75.75
ann 76
[root@localhost ~]# cat while_avg.awk
{
total = 0
i = 2
while ( i <= NF )
{
total += $i
i += 1
}
avg = total / (NF - 1)
print $1,avg
}
可以参考的一个综合的求阶乘的例子:
[root@localhost ~]# ./fact
enter number:5
the fact of 5 is 120
[root@localhost ~]# cat fact
awk '
BEGIN {
printf "enter number:"
}
$1 ~ /^[0-9]+$/ {
number = $1
if ( number == 0 )
fact = 1
else
fact = number
for ( x = number - 1;x > 1;x-- )
fact *= x
printf "the fact of %d is %g \n",number,fact
exit
}
{printf "invalid.enter number again:"}
' -
·影响程序流控制的语句
break:作用于循环语句,跳出循环
continue:作用于循环语句,跳出,进入下一个循环
next:作用于主输入循环,读取下一行输入行,返回到脚本顶部
exit:作用于主输入循环,退出主输入循环,将控制转移给END规则
·数组
选用NR作为数组下标的例子:
[root@localhost ~]# cat array_avg.awk
{
total = 0
for ( i =2; i <= NF; ++i )
total += $i
avg[NR] = total / (NF - 1)
}
END {
for ( x = 1;x <= NR;x++ )
class_avg_total += avg[x]
class_avg = class_avg_total / NR
for ( x = 1;x <=NR;x++ )
if ( avg[x] >= class_avg )
++above_avg
else
++below_avg
print "class avg:",class_avg
print "above avg:",above_avg
print "below avg:",below_avg
}
[root@localhost ~]# awk -f array_avg.awk grade.log
class avg: 72.85
above avg: 3
below avg: 2
·关联数组
数组的下标可以为字符串或数值
可以用于构造类似Python中的字典数据结构。
进行关联数组访问时,其输出数组条目顺序是随机的。
使用关联数组进行达到分数等级的个数的一个例子,综合以上各个内容:
[root@localhost ~]# awk -f fin_array_avg.awk grade.log
andy 79.75 C
lily 62.75 D
lucy 70 C
steve 75.75 C
ann 76 C
class avg: 72.85
above avg: 3
below avg: 2
C: 4
D: 1
[root@localhost ~]# cat fin_array_avg.awk
{
total = 0
for ( i =2; i <= NF; ++i )
total += $i
avg_arr[NR] = total / (NF - 1)
avg = avg_arr[NR]
if (avg >= 90) class = "A"
else if (avg >= 80) class = "B"
else if (avg >= 70) class = "C"
else if (avg >= 60) class = "D"
else class = "E"
++class_arr[class]
print $1,avg,class
}
END {
for ( x = 1;x <= NR;x++ )
class_avg_total += avg_arr[x]
class_avg = class_avg_total / NR
for ( x = 1;x <=NR;x++ )
if ( avg_arr[x] >= class_avg )
++above_avg
else
++below_avg
print "class avg:",class_avg
print "above avg:",above_avg
print "below avg:",below_avg
for ( class_x in class_arr )
print class_x ":",class_arr[class_x] | "sort"
}
·split进行数组创建
[root@localhost ~]# echo 2 |awk -f split_month.awk
2 II
[root@localhost ~]# cat split_month.awk
BEGIN {
split("I,II,III,IV,V",numb,",")
}
$1 > 0 && $1 <= 5 {
print $1,numb[$1]
exit
}
{
print "valid number."
exit
}
·多维数组
模拟引用多维数组
简单的例子:
BEGIN {
for (i = 1;i <= 2; ++i)
for ( j = 1; j <=2; ++j)
bitmap[i,j] = "O"
}
...
·系统变量数组
ARGV:awk+附带的参数(-f参数除外)组成的数组,下标从0开始;
ARGC:参数数组个数。
·函数
算术函数:
三角函数-对数函数-平方根函数-随机函数-取整函数
举例如下:
[root@localhost ~]# awk -f int_func.awk
33.3333
33
[root@localhost ~]# cat int_func.awk
BEGIN {
print 100/3
print int(100/3)
exit
}
字符串函数:
gsub(r,s,t):在t中将r匹配到所有字符串,匹配为s ;(t未输入时,默认为$0)
sub(r,s,t):在t中将r首次匹配到字符串,匹配为s ;(t未输入时,默认为$0)
index(s,t):返回t在s中的位置;否则返回0;
match(s,r):返回正则表达式r在s中的起始位置;否则返回0;
substr(s,p,n):返回s中p位置之后的那个字符;(n未输入时,返回p之后剩余的字符)
split(s,a,sep):将s根据sep分割后写入数组a中,返回数组元素个数;
length(s):返回s的长度;(s未输入时,默认为$0)
sprintf(“fmt”,expr)对于expr使用fmt格式说明
tolower(s):大写转化为小写
toupper(s):小写转化为大写
一些例子:
index和substr综合:
将第一个单词的首字符变为大写:
[root@localhost ~]# echo abc |awk -f index_substr.awk
Abc
[root@localhost ~]# cat index_substr.awk
BEGIN {
upper="ABCDEFGH"
lower="abcdfegh"
}
{
firstchar=substr($1,1,1)
if (charplace = index(lower,firstchar))
$1 = substr(upper,charplace,1)substr($1,2)
print $0
}
替换函数gsub和sub:
gsub的正则表达式替换,&为复制模式字符,\为转义符:(基本规则类似sed)
[root@localhost ~]# echo abc|awk '{gsub(/ab/,"\\start & end\\",$1);print $1}'
\start ab end\c
另外一个例子:
[root@localhost ~]# cat grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
[root@localhost ~]# cat sub_gsub.awk
awk '
{
gsub(/"/,"")
if (sub(/an/,"An")) print
if (sub(/li/,"Li")) print
}' $*
[root@localhost ~]# ./sub_gsub.awk grade.log
Andy 86 78 99 56
Lily 66 70 59 56
Ann 80 78 90 56
match函数:
函数两个系统变量:RSTART,RLENGTH;
RSTART:匹配子串的开始位置;此为返回值,不匹配时,返回为0
RLENGTH:匹配字符串的字符数。
如使用match进行正则表达式的字符大小匹配,不同awk版本的实现不一致;
遇到不区分大小写的情况,如下:
[root@localhost ~]# cat test.awk
/[a-z]/{
print $0
}
[root@localhost ~]# echo ABC|awk -f test.awk
ABC
[root@localhost ~]# echo abc|awk -f test.awk
abc
[root@localhost ~]# echo 123|awk -f test.awk
[root@localhost ~]#
自定义函数:
在脚本的模式规则定义之前声明;
在脚本内表达式可以使用的地方都可以调用自定义函数。
调取函数时,调取函数输入的参数,在被调函数内,使用其的“拷贝”值,不影响该参数调取函数的继续使用;
在被调函数内定义的变量,在调取函数中也可以使用;想要避免该情况,将该变量在被调函数声明时作为形参进行声明即可。
一个简单的例子:
[root@localhost ~]# echo 123 |awk -f fun-test.awk
call fun_test: with test parameter.
name: TEST
[root@localhost ~]# cat fun-test.awk
function fun_test(name)
{
print "name:",name
}
{
print "call fun_test: with test parameter."
fun_test("TEST")
}
·其他函数:
getline函数:
getline和next类似都是读取下一行,但是,有不同点:
next:读取下一行后,返回输入循环首语句;
getline:读取下一行,程序继续进行,不会返回循环首语句。
[root@localhost ~]# awk -f getline-test.awk grade.log
ann 80 78 90 56
[root@localhost ~]# cat getline-test.awk
/steve/{
getline
print $0
}
[root@localhost ~]# cat grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
使用getline读取文件和管道输入:
[root@localhost ~]# echo |awk -f getline-input.awk
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
root :0 2012-12-03 22:05
[root@localhost ~]# cat getline-input.awk
{
while(getline < "grade.log" > 0)
print
"who"|getline me
print me
close("who")
}
system函数:
[root@localhost ~]# awk -f getfile.awk
please input file name:grade.log
andy 86 78 99 56
lily 66 70 59 56
lucy 77 78 69 56
steve 82 78 87 56
ann 80 78 90 56
cat end.
[root@localhost ~]# cat getfile.awk
BEGIN{
printf "please input file name:"
getline < "-"
file=$0
system("cat " file)
print "cat end."
}
print输出重定向到文件和管道:
一个简单的例子:
[root@localhost ~]# awk -f printfile.awk
please input file name:grade.log
grade.log
cat filename end.
file: grade.log word count:
10
[root@localhost ~]# cat printfile.awk
BEGIN{
printf "please input file name:"
getline < "-"
file=$0
print file >"filename"
system("cat filename")
print "cat filename end."
print "file:",file,"word count:"
print file |"wc -c"
}
参考资料:
sed & awk:Doughherty & Robbins.