Chinaunix首页 | 论坛 | 博客
  • 博客访问: 646924
  • 博文数量: 227
  • 博客积分: 8017
  • 博客等级: 中将
  • 技术积分: 2069
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-08 22:50
文章分类

全部博文(227)

文章存档

2011年(10)

2010年(55)

2009年(28)

2008年(134)

我的朋友

分类: LINUX

2008-06-15 23:01:27

一. 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


 
阅读(1073) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~