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比较快