|
一. 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 "<dir>", "\t", $8 # print <dir> 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
|