gawk 模式处理语言 学习笔记

gawk 模式处理语言
特性:
  1 灵活的格式  2 条件执行  3 偱环语句  4 数值变量
  5 字符串变量  6正则表表式  7关系表达式  8 c语言printf
  9 协进程    10 网络数据交换

1.1 语法
  gawk [options][program][file-list]
  gawk [options] -f program-file [file-list]
  gawk可以从指定文件中或标准输入获取输入
  使用getline可以对输入有更多选择
  协进程(coprocess)可以使gawk与另一个程序进行交互或通过网络交换数据

1.2 参数
  参数中的program是用户在命令行中所包含有gawk的程序
  program-file是存放gawk程序的文件
  在命令行上使用gawk要加单引号
  file-list是输入文件如不指定,则由标准输入,getline或协进程获取

1.3 选项
  –field-seperator fs      -F fs        将fs作为输入字段分隔符的值
  –file program-file      -f program-file    存放gawk程序
  –help          -W help      总结如何使用gawk
  –lint            -W lint      对不正确或不利于移植的结构发出警告
  –posix          -W posix      运行POSIX兼容版的gawk
  –traditional        -W traditional    忽略gawk中的新特性与awk兼容
  –assign var=value      -v var=value    给变量赋值

1.4 注意
  很多linux将/bin/awk链接到了/bin/gawk上

1.5 语言基础
  gawk 程序由一行或多行文本构成,其中包含一个模式和动作
  pattern {action}
  模式(pattern)用来从输入中选取文本行,对于模式选中的每行文本,都执行相应的对作
  如程序中不包含模式,gawk将选取所有行,如果不包含动作gawk将把选中的行复制到标准输出

  1.5.1 模式
    用斜杠把正则表达式包装起来,就可以作为模式使用
    ~操作符测试是否匹配正则表达式 !~测试不匹配
    用||或 &&来组合任何模式
    关系操作符包括:<,>,<=,>=,=,!=
    BEGIN与END是两个独特的模式分别是在gawk开始处理之前与处理之完毕之后的命令
    ,逗号,用逗号隔开的两个模式将选取匹配第一个模式行到匹配第二个模式的下一行

  1.5.2 动作
    gawk如果匹配某个模式,它将执行指定动作,如果没有指定动作,那将执行默认(可用{print}表示)如在print后带上参数,gawk将只显示指定参数。可将print输出发送到文件>追加>>或者通过管道发纷呈另一个程序|
    协进程|& 是一个双向的管道,可以与运行在后台的程序交换数据
    逗号用于print命令中把各项区分开
    通过分号可将多个动作隔开,可以在一行上包含多个动作。

  1.5.3 注释
    #是gawk的注释符号

  1.5.4 变量
    gawk的模式部分与动作部分中均可以使用用户变量与程序变量
    $0      当前记录
    $1-$n    当前记录中的字段
    FILENAME  当前输入文件名
    FS      输入字段的分隔符
    NF      当前记录的字段数目
    NR      当前记录的编号
    OFS      输出字段分隔符
    ORS      输出记录分隔符
    RS      输入记录分隔符

  1.5.5 函数
    length(str)      返回str中的字符数,如没参数,返回当前记录字符数
    int(num)        返回 num的整数部分
    index(str1,str2)    返回str2在str1中出现的位置,如不存在返回 0
    split(str,arr,del)    以del做分隔符,将str中的元素放到数组 arr中,返回个数
    sprintf(fmt,args)    根据fmt格式化args并返回格式化后的字符串
    substr(str,pos,len)    返回str 中从pos开始长度为len个字符的字符串
    tolower(str)      返回str副本,并转成小写字母
    toupper(str)      返回str副本,并转成大写字母

  1.5.6 算术操作符
    *,/,%,+,-,=,++,–,+=,-=,*=,/=,%=

  1.5.7 关联数组
    语法:array[string]=value
    可以将特殊的for结构用于关联数组
    语法如下:
    for (elem in array) action
    在for 遍历数组元素时,elem表示数组中每个元素的值,array为数组名称action为gawk对数组中每个元素所采取的动作,可以在action中使用elem变量。详细见1.6节

  1.5.8 printf
    可以使用printf来代替print控制gawk的输出,gawk版的printf类似于 C
    语法:printf "control-string",arg1,arg2,…,argn
      control-string决定了printf如何格式化arg
      可以在contral-string中使用/n来表示换行符,/t表示TAB
      control-string包含了转换规格,每个参数对应一个转换规格语法
      %[-][x[.y]]conv
      其中-使printf将参数左对齐,x表示最小字段宽度
      .y表示数字中小数点右边的位数,conv表示数值转换的类型
      conv        转换类型
      d      十进制
      e      指数表示
      f      浮点数字
      g      使用f或e中较短的那个
      o      无符号八进制
      s      字符串
   &nb

sp;  x      无符号十六进制

  1.5.9 控制结构
    1 if…else
      语法:(else可选)
        if (condition)
          {commands}
        [else
          {commands}]
      例1:if ($5<=5000) print $0
      例2:cat ifl
        BEGIN {
            nam="sam"
            if (nam=="max")
              print "name is max"
            else
              print "name is not max,it is",nam
            }
          gawk -f ifl

    2 while
      语法:while (condition)
          {commands}
      例:cat wh
        BEGIN {
            n=1
            while (n<=5)
              {
                print n"^2",2**n
                n++
              }
            }
        gawk -f wh

    3 for
      语法:for (init;condition;increment)
          {commands}
      例:cat forl
        BEGIN {
            for (n=1;n<=5;n++)
              print n"^2",2**n
            }
        gawk -f forl

      关联数组的for结构
      语法:
        for (var in array)
          {commands}
      例:END {for (name in manf) print name,manf[name]}
      对于关联数组的for 见1.6

    4 break
      结束for或 while最内层循环

    5 continue
      将for 或 while转移到末尾,并断续执行下一次迭代

1.6 示例
  建立一个示例文件 文件包括制造商,型号,生产年代,千里里程数及价格
  cat cars
  plym  fury      1970  73  2500
  chevy  malibu    1990  60  3000
  ford    mustang    1965  45  10000
  volvo  s80      1998  102  9850
  ford    thundbd    2003  15  10500
  chevy  malibu    2000  50  3500
  bmw  325i      1985  115  450
  honda  accord    2001  30  6000
  ford    taurus    2004  10  17000
  toyota  rav4      2002  180  750
  chevy  impata    1985  85  1550
  ford    explor    2003  25  9500
  

例1:
[root@redhat Testsh]# awk '{print}' cars
plym fury 1970 73 2500
chevy malibu 1990 60 3000
ford mustang 1965 45 10000
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
chevy malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
ford taurus 2004 10 17000
toyota rav4 2002 180 750
chevy impata 1985 85 1550
ford explor 2003 25 9500
这是一个缺失模式,因为没有模式,gawk把输入中所有行复制到了输出

例2:
[root@redhat Testsh]# awk '/chevy/' cars
chevy malibu 1990 60 3000
chevy malibu 2000 50 3500
chevy impata 1985 85 1550
缺失模式,只有一个模式,但没有明确动作。斜杠指出chevy是一个正则表达式。在这种情况中将执行默认动作print.将输入中包含chevy的文本行复制到输出
注意:单引号可以阻止某此问题的发生(比如命令行由bash扩展了,导制传递给awk的参数出错)

例3:
[root@redhat Testsh]# awk '{print $3,$1}' cars
1970 plym
1990 chevy
1965 ford
1998 volvo
2003 ford
2000 chevy
1985 bmw
2001 honda
2004 ford
2002 toyota
1985 chevy
2003 ford
显示第三个字段与第一个字段

例4:
[root@redhat Testsh]# gawk '/chevy/ {print $3,$1}' cars
1990 chevy
2000 chevy
1985 chevy
显示包含chevy行的第三个字段与第一个字段

例5:
[root@redhat Testsh]# awk '/h/' cars
chevy malibu 1990 60 3000
ford thundbd 2003 15 10500
chevy malibu 2000 50 3500
honda accord 2001 30 6000
chevy impata 1985 85 1550
显示包含字母h的行

例6:
[root@redhat Testsh]# gawk '$1~/h/' cars
chevy malibu 1990 60 3000
chevy malibu 2000 50 3500
honda accord 2001 30 6000
chevy impata 1985 85 1550
显示第一个字段包含h的行

例7:
[root@redhat Testsh]# gawk '$1~/^h/' cars
honda accord 2001 30 6000
显示第一个字段第一个字母是h的行

例8:
[root@redhat Testsh]# gawk '$2~/^[tm]/ {print $3,$2,"$"$5}' cars
1990 malibu $3000
1965 mustang $10000
2003 thundbd $10500
2000 malibu $3500
2004 taurus $17000
选中第2个字段以t或m开头的行,并显示第3个字段和第2个字段, 1个美元符号以及第5个字段

例9:
[root@redhat Testsh]# gawk '$3~/5$/ {print $3,$1,"$"$5}' cars
1965 ford $10000
1985 bmw $450
1985 chevy $1550
$使用的三个作用,5$是对行尾或字段尾进行匹配,$加数字的方式表示某字段,"$"代表它自己

例10:
[root@redhat Testsh]# gawk '$3==1985' cars
bmw 325i 1985 115 450
chevy impata 1985 85 1550
显示第三个字段为1985的行

例11:
[root@redhat Testsh]# awk '$5<=3000' cars
plym fury 1970 73 2500
chevy malibu 1990 60 3000
bmw 325i 1985 115 450
toyota rav4 2002 180 750
chevy impata 1985 85 1550
列出字段5小于等于3000的行

例12:
[root@redhat Testsh]# awk '$5>2000 && $5<9000' cars
plym fury 1970 73 2500
chevy malibu 1990 60 3000
chevy malibu 2000 50 3500
honda accord 2001 30 6000
列出$5数值是2000与9000之间的行

[root@redhat Testsh]# awk '$5>"2000" && $5<"9000"' cars
plym fury 1970 73 2500
chevy malibu 1990 60 3000
chevy malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
toyota rav4 2002 180 750
列出$5数值是2000与9000之间的行,但由于2000与9000加了双引号,即进行正文比较(使用ASCII比较)

例13:
[root@redhat Testsh]# awk '/volvo/,/bmw/' cars
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
chevy malibu 2000 50 3500
bmw 325i 1985 115 450
选中由逗号之前的模式指定的行致逗号之后模式匹配的行,如果没有匹配逗号之后的文本行,则会将选取到输入末尾

[root@redhat Testsh]# cat cars
plym fury 1970 73 2500
chevy malibu 1990 60 3000
ford mustang 1965 45 10000
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
chevy malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
ford taurus 2004 10 17000
toyota rav4 2002 180 750
chevy impata 1985 85 1550
ford explor 2003 25 9500

[root@redhat Testsh]# awk '/chevy/,/ford/' cars
chevy malibu 1990 60 3000
ford mustang 1965 45 10000
chevy malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
ford taurus 2004 10 17000
chevy impata 1985 85 1550
ford explor 2003 25 9500
上边例子在找到第一组文本行之后,它将再次进行处理,查找匹配逗号异军突起前的文本行。awk查找落入chevy和ford之间的所有的三组文本行。尽管输入的每5行包含ford,但gawk并没有选择它,因为此时它正在处理第5行,正在搜索chevy

例14:
[root@redhat Testsh]# cat awk_t
BEGIN {print "make model year miles pricen"}
{print}

[root@redhat Testsh]# awk -f awk_t cars
make model year miles price

plym fury 1970 73 2500
chevy malibu 1990 60 3000
ford mustang 1965 45 10000
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
chevy malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
ford taurus 2004 10 17000
toyota rav4 2002 180 750
chevy impata 1985 85 1550
ford explor 2003 25 9500
一个简单的用-f 参数指定 awk程序文件的例子

例15: (length的使用)
[root@redhat Testsh]# awk '{print length"t",$0}' cars|sort -n
21 bmw 325i 1985 115 450
22 plym fury 1970 73 2500
23 volvo s80 1998 102 9850
24 ford explor 2003 25 9500
24 toyota rav4 2002 180 750
25 chevy impata 1985 85 1550
25 chevy malibu 1990 60 3000
25 chevy malibu 2000 50 3500
25 ford taurus 2004 10 17000
25 honda accord 2001 30 6000
26 ford mustang 1965 45 10000
26 ford thundbd 2003 15 10500
此例使用了length函数来显示每行的字符个数,t是一个TAB,用来对齐
sort -n 对数字进行排序

例16:(NR的意义:行编号)
[root@redhat Testsh]# awk 'length>24 {print NR"t",$0}' cars
2 chevy malibu 1990 60 3000
3 ford mustang 1965 45 10000
5 ford thundbd 2003 15 10500
6 chevy malibu 2000 50 3500
8 honda accord 2001 30 6000
9 ford taurus 2004 10 17000
11 chevy impata 1985 85 1550
显示出字符个数大于24的行,并显示行号

例17:
[root@redhat Testsh]# awk 'NR==2,NR==4' cars
chevy malibu 1990 60 3000
ford mustang 1965 45 10000
volvo s80 1998 102 9850
显示从第二行到第四行
例18: (END的使用)
[root@redhat Testsh]# awk '{print}END{print "—END—"}' cars
plym fury 1970 73 2500
chevy malibu 1990 60 3000
ford mustang 1965 45 10000
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
chevy malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
ford taurus 2004 10 17000
toyota rav4 2002 180 750
chevy impata 1985 85 1550
ford explor 2003 25 9500
—END—
在处理完输入之后才执行END指定的语句

例19:
[root@redhat Testsh]# cat awk_t1
{
if ($1~/ply/) $1="plyssj"
if ($1~/chev/) $1="chevssj"
print
}
[root@redhat Testsh]# awk -f awk_t1 cars
plyssj fury 1970 73 2500
chevssj malibu 1990 60 3000
ford mustang 1965 45 10000
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
chevssj malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
ford taurus 2004 10 17000
toyota rav4 2002 180 750
chevssj impata 1985 85 1550
ford explor 2003 25 9500
给$1赋值的例子

例20:(建立独立的awk脚本)
[root@redhat Testsh]# cat if.ak
#!/bin/awk -f
{
if ($1~/ply/) $1="plyssj"
if ($1~/chev/) $1="chevssj"
print
}
[root@redhat Testsh]# if.ak cars
plyssj fury 1970 73 2500
chevssj malibu 1990 60 3000
ford mustang 1965 45 10000
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
chevssj malibu 2000 50 3500
bmw 325i 1985 115 450
honda accord 2001 30 6000
ford taurus 2004 10 17000
toyota rav4 2002 180 750
chevssj impata 1985 85 1550
ford explor 2003 25 9500

例21:(printf的使用)
[root@redhat Testsh]# cat if1.ak
#!/bin/awk -f
BEGIN{
print " Miles"
print "Make Model year (000) price"
print "——————————————-"}
{if ($1~/ply/) $1="plyssj"
if ($1~/chev/) $1="chevssj"
printf "%-10s %-8s %-5d %-5d $ %8.2fn",$1,$2,$3,$4,$5
}
[root@redhat Testsh]# if1.ak cars
Miles
Make Model year (000) price
——————————————-
plyssj fury 1970 73 $ 2500.00
chevssj malibu 1990 60 $ 3000.00
ford mustang 1965 45 $ 10000.00
volvo s80 1998 102 $ 9850.00
ford thundbd 2003 15 $ 10500.00
chevssj malibu 2000 50 $ 3500.00
bmw 325i 1985 115 $ 450.00
honda accord 2001 30 $ 6000.00
ford taurus 2004 10 $ 17000.00
toyota rav4 2002 180 $ 750.00
chevssj impata 1985 85 $ 1550.00
ford explor 2003 25 $ 9500.00
例22:(重定向输出)
[root@redhat Testsh]# cat awk_out
/chevy/ {print > "chevfile"}
/ford/ {print > "fordfile"}
[root@redhat Testsh]# awk -f awk_out cars
[root@redhat Testsh]# cat chevfile;echo "";cat fordfile
chevy malibu 1990 60 3000
chevy malibu 2000 50 3500
chevy impata 1985 85 1550

ford mustang 1965 45 10000
ford thundbd 2003 15 10500
ford taurus 2004 10 17000
ford explor 2003 25 9500

例23:(使用FS )
[root@redhat Testsh]# cat find_uid
#!/bin/awk -f
BEGIN{FS=":"
saveit=0}
$3>saveit {saveit=$3}
END{print "Max UID is " saveit }
[root@redhat Testsh]# find_uid /etc/passwd
Max UID is 65534
[root@redhat Testsh]# awk -F: '{print $3}' /etc/passwd |sort -n|tail -5
556
557
558
559
65534

例24:
[root@redhat Testsh]# cat manuf
#!/bin/bash
gawk '{manuf[$1]++} END {for (name in manuf) print name,manuf[name]}' cars |sort
[root@redhat Testsh]# bash ./manuf
bmw 1
chevy 3
ford 4
honda 1
plym 1
toyota 1
volvo 1
关联数组使用cars文件中每条记录的第1个字段的内容作为索引。这个数组由元素manuf[plym],manuf[chevy]等组成。每个元素在创建时都被初始化为0

例25:
[root@redhat Testsh]# cat word_usage
#!/bin/bash
tr -cs '[a-zA-Z]' '[n*]' < $1|gawk '{count[$1]++}END {for (item in count) printf "%-15s %-4d n", item,count[item]}'|sort +1nr +0f -1
[root@redhat Testsh]# bash ./word_usage text.txt |head -5
the 64
is 35
of 28
a 21
and 21
……
显示文件中单词的使用情况列表

例26:
[root@redhat Testsh]# cat report
#!/bin/bash
if [ $# == 0 ] ;then
echo "you must supply a filename"
exit 1
fi
(date;cat $1)|awk '
NR==1 {print $6,$2,$3,$4}
NR>1 {printf "%-10s%-15sn", $5,$1}'
[root@redhat Testsh]# bash ./report cars
2009 7月 30 10:55:07
2500 plym
3000 chevy
10000 ford
9850 volvo
10500 ford
3500 chevy
450 bmw
6000 honda
17000 ford
750 toyota
1550 chevy
9500 ford
一个把日期加入报告中的办法
getline与协进程先不讨论了,一般用不到

练习:
1 编写一个gawk程序对文件中的每行编号,然后将它的输出发送到标准输出
[root@redhat root]# cat g4
{print NR,$0
}
[root@redhat root]# cat tt
aaaa 1111
bbbb 2222
cccc 3333
dddd 4444
[root@redhat root]# gawk -f g4 <tt
1 aaaa 1111
2 bbbb 2222
3 cccc 3333
4 dddd 4444

2 编写一个gawk程序,显示第1个字段中的字符数目,后面跟着第1个字段,然后将输出发送到标准输出
[root@redhat root]# cat g5
{print length($1),$1
}
[root@redhat root]# cat tt
aaaa 1111
bbbb 2222
cccc 3333
dddd 4444
[root@redhat root]# gawk -f g5 <tt
4 aaaa
4 bbbb
4 cccc
4 dddd

3 编写一个gawk程序,使用cars文件,显示所有价格高于5000美元的汽车,然后将其输出发送到标准输出
[root@redhat root]# awk -f g5 < /root/Testsh/cars
ford mustang 1965 45 10000
volvo s80 1998 102 9850
ford thundbd 2003 15 10500
honda accord 2001 30 6000
ford taurus 2004 10 17000
ford explor 2003 25 9500

4 使用gawk来判断/etc/termcap中有多少行包含了字符串vt100.使用grep验证一下自己的程序
[root@redhat root]# cat g5
{if ($0~/vt100/)
num+=1
}
END{print num}
[root@redhat root]# time awk -f g5 /etc/termcap
174

real 0m0.142s
user 0m0.140s
sys 0m0.010s

验证:

[root@redhat root]# time grep -c vt100 < /etc/termcap
174

real 0m0.317s
user 0m0.310s
sys 0m0.010s

还是awk比较快

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注