`
xiangzi21
  • 浏览: 29573 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

sed与awk菜鸟的笔记--第七章 编写awk脚本

阅读更多

第七章 编写awk脚本


规则:
awk脚本分三部分 (BEGIN,END必须大写)
1.BEGIN段:主循环处理前(不管主循环有没有数据)
2.主循环段:主循环处理(文件行数)
3.END段:主循环处理后(不管主循环有没有数据)


模式匹配:
当awk读入输入行时,只有与模式匹配才能执行指定操作,如果没有指定操作,匹配的行会被打印出来.
$ awk '/^$/ {print "this is a blank line"}' file #如果file匹配到空行即打印"this is a blank line"(即文件有几个空行便打印几句"this...").
$ awk '/AA/ ' file     #如果file匹配到"AA"即打印匹配行的内容(因为没指定命令).


#以下脚本判断是整数,字符串还是空行.(注意:print打印的不能用单引号)
$ cat test.awk
/[0-9]+/ {print "integer"}
/[a-zA-Z]+/ {print "string"}
/^$/ {print "blank"}


#脚本执行效果如下,脚本判断输入的是int还是string或空.当输入"4r4"的时候,一行匹配多条规则.只输入回车的时候显示"blank".
$ awk -f test.awk
43  #输入
integer
t  #输入
string
  #输入
blank
4r4  #输入
integer
string


程序脚本注释:
以"#"开始,换行符结束.和sed不同,awk可以在程序任何地方添加注释.
在命令行中运行awk程序,则不能用单引号,否则会被shell解析.(将awk写成文件脚本貌似也不可以).


记录和字段:
对于每一行来说,可将行分为多个字段.默认以空格或制表符来作为分隔符.
#此记录即为3个字段,以空格或制表符分隔
$ cat file
A B C


字段的引用和分离:
用$来指定字段.在该操作符后面跟数字或变量,用于标识符的位置.
#以下print以逗号做分隔符,输出时默认为空格(以后可讲到用OFS设置其他分隔符).若不用逗号则无分隔符.
$ cat file
A B C

$ awk '{print $2,$1,$3}' file
B A C

$ awk '{print $2      $1   $3}' file
BAC


#以下$(ONE+TWO) 等价于$3.可以用任何计算值为整数表达式表示一个字段,而不只是用数字或变量.
$ echo a b c | awk 'BEGIN {ONE = 1;TWO = 2} {print $(ONE+TWO)} '
c


可以在命令行中用-F参数设置分隔符
#以下将制表符设置为分隔符,file中只有一个制表符,所以该制表符前面为$1,后面为$2,$3为空
$ cat file
A B C

$ awk -F'\t' '{print $1}' file
A B

$ awk -F'\t' '{print $2}' file
C

$ awk -F'\t' '{print $3}' file


在脚本中指定域分隔符(字段分隔符)是个很好的习惯.指定必须在读取第一个输入行前执行,所以要在BEGIN中执行.
BEGIN {FS=","} #指定逗号为域分隔符(与在命令行中加参数"-F"效果相同,等同于awk -F ',')

$cat game
I,love,this,game!

$cat awk1
BEGIN {FS = ","}
{print $1 "INS   ERT" $3}

$awk -f awk1 game
IINS   ERTthis
 
使用(~)可以测试一个字段的正则表达式,(!~)表示取反
$5 ~ /AA/ {print $0} #将第五列匹配AA的整行打印
$5 !~ /AA/ {print $1} #打印出第五列不匹配AA的行的第一列(即如果改行第五列不匹配AA,那么打印该行的第一列)


字段的划分:完整的问题

可以用正则表达式指定分隔符
FS = "\t" #使用一个制表符作为分隔符
FS = "\t+" #使用一个或多个制表符作为分隔符
FS = "[,:\t]" #在中括号里的三个字符都可以被解释为分隔符

#以下以逗号或冒号为分隔符,将字符串"a,b:cdd:e"分为四个域a b cdd e
$ echo "a,b:cdd:e"|awk -F'[,:]' '{print $1,$2,$3}'
a b cdd


表达式:
可以使用表达式来储存,操作和检索数据,这些操作与在sed中有很大区别.
p170有转义符号
变量有两种类型,数字型和字符串型.变量不需要声明,默认为0或空串.
z = "Hello"  #将字符串"Hello"赋给变量z
z = "Hello" "World" #空格为连接操作符.前面表达含义为:将字符串"HelloWorld"赋给变量z
z = $1   #美元符号($)是引用字段操作.前面表达含义为:将输入的第一个字段赋给变量z

算术表达式
+(加)
-(减)
*(乘)
/(除)
%(取模)
^(取幂)


赋值操作符
++ 变量加一
-- 变量减一
+= 加结果赋给变量(以下略)
-=
*=
/=
%=
^=
**=


#有空行便打印变量x ,
/^$/ {print x += 1} 或/^$/ {print ++x}


#统计空行数,统计最终结果后打印变量
/^$/ {
++x
}
END{
 print x
}


#计算学生平均成绩
$ cat file
Tom 90 85 77
Jerry 99 73 90

$ cat grades.awk
{ total = $2 + $3 + $4
  print $1,total/3
}

$ awk -f grades.awk file
Tom 84
Jerry 87.3333


系统变量:
FS:输入字段分隔符(默认为空格)
OFS:输出字段分隔符(默认为空格)
NF:输入记录的字段个数(每行分别的字段个数),$NF为每输入行的最后一个字段
RS:输入行分隔符(默认为一个换行符)
ORS:输出行分隔符(默认为一个换行符)
FILENAME:当前输入文件的名称
FNR:多个输入文件时,表示与当前输入文件相关的当前记录代码.
NR:到目前为止输入的行数,如果写在END里,则为输入多少行

处理多行记录:
下面的例子演示读入一个记录,而记录中每个字段都是单独成一行.
#文件中分别为姓名,性别,年龄,籍贯
#脚本将字段分隔符FS设置为换行符,记录分隔符RS设置为空字符串,并打印第一和最后一个字段.

$ cat message
Tom
male
25
Liaoning


Jerry
male
20
Shandong


$ cat rows.awk
BEGIN{ FS="\n" ;RS="" }
{print $1,$NF}


$ awk -f rows.awk message
Tom Liaoning
Jerry Shandong


#下面是将输出列分隔符OFS设置为换行符,记录分隔符ORS为空行(即需要两个换行符)
$ cat rows.awk
BEGIN{ FS="\n" ;RS="" ;OFS="\n";ORS="\n\n" }
{print $1,$NF}


$ awk -f rows.awk message
Tom
Liaoning


Jerry
Shandong

p179支票结算示例的简单脚本,这里不举例了.


关系操作符和布尔操作符
<,>,<=,>=,==,!=,~(匹配),!~(不匹配)

NF==6 {print $1,$6} #只有具有六个字段的记录才被打印,并打印出第一和第六个字段

正则表达式可以用变量来提供,这样使代码更通用.


布尔操作符
&&,||,!
&&的优先级高于||


格式化打印
printf(format_expression[,argument]) :printf与C语言中的类似.
其中圆括号是可选的.format_expression是描述格式的表达式,通常为引号括起的字符常量形式.[,argument]是一个参数列表,在格式说明前有个百分号

常用说明符:
c:ASCⅡ字符
d:十进制整数
f:浮点格式
g:e或f的转换形式,长度最短,末尾的0被去掉
s:字符串
u:无符号十进制
%:字面字符%


#下面示例输出十进制的$5,然后是制表符,然后是字符串$9,然后是换行符.
#当使用printf时,必须提供换行符,否则永远不会换行.对每个格式必须提供一个相应的参数.
printf("%d\t%s\n",$5,$9)

printf可以规定输出域可宽度和对齐方式.一个表达式由3个可选的修饰符组成,跟在%后面,并在格式说明符之前.
%-width.precision format-specifier
width是宽度,默认为右对齐,必须指定"-"来设置左对齐.
"%-20s"的意思是左对齐的一个域,长度为20个字符的字符串.不够则用空格补.如果大于20则都输出.
precision修饰符用于十进制或浮点数时,用于控制小数点右边的数字位数.对于字符串型,则用于控制打印字符的最大数量.
数值的默认precision值为"%.6g"
可以通过print或pringf参数列表的值动态指定宽度width和精度precision,通过用星号代替实际的值来实现该功能.
#下例中宽度为5,精度为3,要打印的值来至myvar
printf("%*.*g\n",5,3,myvar)
$ echo a |awk '{printf("%*.*g\n",5,3,4000)}'
4e+03
$ echo a |awk '{printf("%*.*g\n",5,3,400)}'
  400


向脚本传递参数
awk 'script' var-value inputfile
在命令行上,参数放在脚本的后面,输入文件的前面
参数每一项都作为单一参数来解析,所以等号两边不能有空格.
#以下例子为脚本scriptfile传递两个参数.
$ awk -f scriptfile high=100 low=100 datafile

#如果将awk脚本写入shell脚本,则以上脚本方法如下
$ cat awk.sh
awk -f scriptfile "high=$1" "low=$2" datafile

#$1和$2分别为执行shell脚本时的输入参数,以上脚本执行方法如下
$ sh awk.sh 100 100

环境变量的输出结果也可以作为变量值传递
awk '{...}' name=$HOSTNAME file  #将主机名当做参数传给脚本
awk '{...}' dir=`pwd` file  #将当前目录作为参数传给脚本

#也可以使用命令行参数定义系统变量(就不用在HEAD里定义了),下面执行结果相同,但最好用单引号括起来.当记录分隔符中要设置空格时,就必须要用单引号了.
$ awk '{print NR,$0}' OFS='.' file
$ awk '{print NR,$0}' OFS=. file

命令行参数的一个重要限制是它们在BEGIN过程中是不可用的.也就是说BEGIN执行后它们才会被读入.
#下面为命令行输入,执行顺序为:
1.执行BEGIN的操作 (由于变量n是空,则打印空行)
2.读入第一个参数 n=1
3.读入第二个参数 file1
4.对file1逐行执行脚本
5.读入第三个参数n=2
6.对file2逐行执行脚本
如果file1有10行,file2有5行,那么下面命令执行后的结果应该为:第一行为空行,然后下面10行"one",再下面是5行"two"


$ awk 'BEGIN {print n}
{
if (n==1) print "one"
if (n==2) print "two"
}' n=1 file1 n=2 file2

one
two

如果想在BEGIN之前设置输入参数,则要用-v参数
$ awk -v n=1 'BEGIN {print n}' file #
$ awk 'BEGIN {print n}' file n=1

 

 

 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics