一. writing scripts with awk
1. awk的hello,world
方式一: $ echo 'this line of data is ignored' > test
$ awk '{ print "Hello, world" }' test
方式二: $ cat test2
Hello, world
$ awk '{ print }' test2
Hello, world
方式三: $ awk 'BEGIN { print "Hello, world" }'
Hello, world
2. awk模式匹配
实例:
$cat test
mytest
234
$cat awkscr.awk
#awkscr.awk test for integer, string or empty line.
/^$/ {print "blank line!"}
/[0-9]+/ {print "Number!"}
/[A-Za-z]+/ {print "string!"}
$ awk -f awkscr.awk test
blank line!
string!
Number!
blank line!
注意: a. 写好注释, 由#开头的都是注释
b. awk脚步中最好不要用单引号。 否则在通过命令行来执行该脚步的时候会被shell解释。
3. 记录和域
awk将输入的每一行看作一条记录(record), 而每个单词(word)看作一个域(field), 这些field由空格或者TAB分离。 当然可以指定其他的delimiter.
3.1 引用和分割域
实例1:
$cat names
John Robinson 666-555-1111
$awk '{print $2, $1, $3}' names
Robinson John 666-555-1111
解释:$0 引用整行,$1,$2...分别代表第一个,第二个域
实例2:
$ echo a b c d | awk 'BEGIN { one = 1; two = 2 }
> { print $(one + two) }'
c
在命令行中可以通过-F选项更改域分隔符:
$ awk -F"\t" '{ print $2 }' names
666-555-1111
实例3:
$cat names
John Robinson,Koren Inc.,978 4th Ave.,Boston,MA 01760,696-0987
Phyllis Chapman,GVE Corp.,34 Sea Drive,Amesbury,MA 01881,879-0900
$cat blocklist.awk
# blocklist.awk -- print name and address in block form.
# input file -- name, company, street, city, state and zip, phone
{ print "" # output blank line
print $1 # name
print $2 # company
print $3 # street
print $4, $5 # city, state zip
}
$awk -F, -f blocklist.awk names
John Robinson
Koren Inc.
978 4th Ave.
Boston MA 01760
Phyllis Chapman
GVE Corp.
34 Sea Drive
Amesbury MA 01881
不过有时候直接在脚本中指定分割符比较好, 可用通过: BEGIN { FS = "," } 指定域分割符为,
实例4:
$cat phonelist.awk
# phonelist.awk -- print name and phone number.
# input file -- name, company, street, city, state and zip, phone
BEGIN { FS = "," } # comma-delimited fields
{ print $1 ", " $6 }
$awk -f phonelist.awk names
John Robinson, 696-0987
Phyllis Chapman, 879-0900
不过模式匹配都是对整行的所有记录进行匹配。 如果只希望配置某个特定的域,则可以通过~来实现:
$5 ~ /MA/ { print $1 ", " $6 }
匹配第五个域中含有MA的行,在匹配到后, 打印第一和第六个域的值。 后者通过!~来实现相反的意思:
$5 !~ /MA/ { print $1 ", " $6 }
用于匹配所有的行中第五个域不含MA的行.
3.2 域分割:
可用通过三种方式设置域的分割:
a. 设置FS为单个空格: 这是默认的方式, 这样awk会通过空格或者TAB或者两者的组合来分割域。
b. 设置FS为单个字符: 例如在出来/etc/passwd文件的时候,可以将:设置为分割符
c. 设置FS为多个字符: 这时awk会将起解释为正则表达式。
实例:
FS="\t"
awk将会将TAB作为分割符, 这时候: abc\t\tdef 将有3个域
FS="\t+"
awk将会将TAB或者联系的几个TAB作为分割符, 这是 abc\t\tdef就只有两个域了。
通过正则表达式还可以指定多个分割符:
FS="[':\t]"
这样就指定了三个可用的分割符: 分号,冒号,和TAB
4. 表达式
awk中的表达式跟C语言差不多。
1). 转义字符:
\a 鸣叫 \t 制表符 \n 换行等等...
2) 变量: awk中的变量不需要声明,直接使用就行了。 不过大小写敏感。 跟C语言一样,可用用字母,数字,下划线组成变量名,但是不能以数字开头。 同时awk变量也不需要指定类型。 awk变量只有string 和 numeric两种,awk会根据根据表达式的环境自动决定变量的类型。
变量赋值: x = 1
z = "hello"
y = "Hello" "World" ##空格用于连接字符串, 所以这里z="HelloWorld"
一定要记得 $ 符号是用来引用域的。
w = $1 ##将第一个域的值赋给w
3) 数字运算符
+, -, *, \, %, ^(乘方) ...
++, --, +=, -=, *=, \=, ^= ...
实例:
#count blank lines
/^$/ { print x += 1} ##这里x自动初始化为0
实例: 计算成绩平均值
$cat grades
john 85 92 78 94 88
andrea 89 90 75 90 86
jasper 84 88 80 92 84
$cat grades.awk
##grades.awk: average five grades
{
total = $2 + $3 + $4 + $5 + $6
avg = total / 5
print $1, avg
}
$awk -f grades.awk grades
john 87.4
andrea 86
jasper 85.6
5. 系统变量
Awk有两种类型的系统(内建)变量:
a. 可改变的系统变量, 例如域分割符,记录分割符等等。。。
b. 第二种是处理过程中可以使用的变量,例如域的个数,当前处理的记录数等。。。
一些常用的系统变量:
FS 输入域分割符
OFS 输出域分割符
NF 当前正在处理的记录所含有的域的个数
RS 输入行分割符
ORS 输出行分割符
FILENAME 当前的输入文件的名字
FNR 相对于当前文件的当前正在处理的记录序号,从1开始
NR 当前正在处理的记录序号,从1开始
OFMT 数字的输出格式(默认值是%.6g)
ARGC 命令行参数的数目
ARGV 包含命令行参数的数组
实例:
我们更改一下上一节中的打印平均成绩的脚本:
print NR ".", $1, avg
运行结果如下:
1. john 87.4
2. andrea 86
3. jasper 85.6
在最后读入最后一个记录后,NR变量记录了当前文件的记录的总个数。 这可以用来report以些东西。 更改以前的phonelist.awk脚本:
# phonelist.awk -- print name and phone number.
# input file -- name, company, street, city, state and zip, phone
BEGIN { FS = "," } # comma-delimited fields
{ print $1 ", " $6 }
END { print ""
print NR, "records processed."
}
运行结果如下:
John Robinson, 696-0987
Phyllis Chapman, 879-0900
2 records processed.
实例二:
当在print语句中用逗号分割各个要打印的表达式时, 输出分割符(OFS)就起了作用:
print NR ".", $1, avg
这时如果设置了OFS为TAB,则输出如下:
1. john 87.4
2. andrea 86
3. jasper 85.6
6. 关系和布尔运算
1) 关系运算
>, <, ==, !=, >=, <=, ~(匹配), !~(不匹配)
关系运算符还可以用来控制action, 例如:
NF==6 { print $1, $6} ##仅当遇到有6个域的行时才会执行后面的动作
2)Bool运算符
||, && ,!
运用实例如下: NR==6||NR>1
(NR > 1 && NF >= 2) || $1 ~ /\t/
! (NR > 1 && NF > 3)
3)获得文件信息:
ls -l $* | awk '{ print $5, "\t", $8 }' ##$*在shell中代表所有的命令行参数,如果没有参数,则其数字为空.
一个长一点的脚本:
ls -l $* | awk '
# filesum: list files and total size in bytes
# input: long listing produced by "ls -l"
#1 output column headers
BEGIN { print "BYTES", "\t", "FILE" }
#2 test for 8 fields; files begin with "-"
NF == 8 && /^-/ {
sum += $5 # accumulate size of file
++filenum # count number of files
print $5, "\t", $8 # print size and filename
}
#3 test for 8 fields; directory begins with "d"
NF == 8 && /^d/ {
print "
", "\t", $8 # print and name
}
#4 test for ls -lR line ./dir:
$1 ~ /^\..*:$/ {
print "\t" $0 # print that line preceded by tab
}'
7. 格式化打印
print 不能进行多样的格式化,仅仅能打印出表达式的值,在末尾增加一个换行符
而从C语言中借鉴过来的printf可以做到格式化输出。
printf的格式跟C语言的一样, 这里就不多说了。
eg: awk 'BEGIN { printf ("Hello, world\n") }'
printf("%d\t%s\n", $5, $9)
printf("%*.*g\n", 5, 3, myvar);
printf("%-15s\t%10d\n", $8, $5) ##print filename and size
8. 给脚本传递参数
1) awk script.awk var=value inputfile
注意: 这时var和value与等号之间无空格。
eg: awk -f scriptfile high=100 low=60 datafile
2) 通过环境变量或者命令输出传递参数
eg: awk '{ ... }' directory=$cwd file1 ...
awk '{ ... }' directory=`pwd` file1 ...
$ awk '{ print NR, $0 }' OFS='. ' names
1. Tom 656-5789
2. Dale 653-2133
3. Mary 543-1122
4. Joe 543-2211
3) 将shell的参数传给awk
eg: awk -f scriptfile "high=$1" "low=$2" datafile ##这里将shell脚本的第一,第二个参数传送high和low.
4)注意: 通过命令行传递的参数值无法在BEGIN{...}中使用。 这是因为Awk将命令行参数当作文件名来处理,所以只有开始处理输入时才会给参数赋值。不过POSIX awk通过-v参数可以在读入文件之前给参数赋值。
例如:
awk 'BEGIN { print n } ##这里的n是空值
{
if (n == 1) print "Reading the first file"
if (n == 2) print "Reading the second file"
}' n=1 test n=2 test2
解释:在处理test的时候,n=1. 在处理test2的时候,n变为2
阅读(1062) | 评论(0) | 转发(0) |