第一部分:Linux命令行
    《Linux命令行与shell脚本编程大全》 第一章:初识Linux shell
    《Linux命令行与shell脚本编程大全》 第二章:走进shell
    《Linux命令行与shell脚本编程大全》 第三章:基本的bash shell命令
    《Linux命令行与shell脚本编程大全》 第四章:更多的bash shell命令
    《Linux命令行与shell脚本编程大全》 第五章:使用Linux环境变量
    《Linux命令行与shell脚本编程大全》 第六章:理解Linux文件权限
    《Linux命令行与shell脚本编程大全》 第七章:管理文件系统
    《Linux命令行与shell脚本编程大全》 第八章:安装软件程序
    《Linux命令行与shell脚本编程大全》 第九章:使用编辑器

    第二部分:shell脚本编程基础
    《Linux命令行与shell脚本编程大全》 第十章:构建基本脚本
    《Linux命令行与shell脚本编程大全》 第十一章:使用结构化命令
    《Linux命令行与shell脚本编程大全》 第十二章:更多的结构化命令
    《Linux命令行与shell脚本编程大全》 第十三章:处理用户输入
    《Linux命令行与shell脚本编程大全》 第十四章:呈现数据
    《Linux命令行与shell脚本编程大全》 第十五章:控制脚本

    第三部分:高级shell编程
    《Linux命令行与shell脚本编程大全》 第十六章:创建函数
    《Linux命令行与shell脚本编程大全》 第十七章:图形化桌面上的脚本编程
    《Linux命令行与shell脚本编程大全》 第十八章:初识sed和gawk
    《Linux命令行与shell脚本编程大全》 第十九章:正则表达式
    《Linux命令行与shell脚本编程大全》 第二十章:sed进阶
    《Linux命令行与shell脚本编程大全》 第二十一章:gawk进阶
    《Linux命令行与shell脚本编程大全》 第二十二章:使用其他shell

    第四部分:高级shell脚本编程主题
    《Linux命令行与shell脚本编程大全》 第二十三章:使用数据库
    《Linux命令行与shell脚本编程大全》 第二十四章:使用Web
    《Linux命令行与shell脚本编程大全》 第二十五章:使用E-mail
    《Linux命令行与shell脚本编程大全》 第二十六章:编写脚本实用工具
    《Linux命令行与shell脚本编程大全》 第二十七章:shell脚本编程进阶


    第二十一章:gawk进阶

    使用变量

    gawk支持两种类型的变量:

    内建变量

    自定义变量

    内建变量

    字段和数据行分割符变量

    使用美元符号($)和数据字段在数据行中位置对应的数值来引用该数据行中的字段。

    比如,要引用数据行中的第二个字段就使用$2

    gawk数据字段和数据行变量

    变量 描述
    FIELDWIDTHS 由空格分隔开的定义了每个数据字段确切宽度的一列数字
    FS 输入字段分割符
    RS 输入数据行分割符
    OFS 输入字段分隔符
    ORS 输入数据行分隔符

    OFS默认值为空格

      $ cat column_data.txt
    1987-06-05
    $ gawk 'BEGIN{FS="-"} {print $1,$2,$3}' column_data.txt 
    1987 06 05
    $ gawk 'BEGIN{FS="-";OFS=":"} {print $1,$2,$3}' column_data.txt 
    1987:06:05
     

    一旦设置了FIELDWIDTHS,FS就会失效

    一旦设置了 FIELDWIDTHS值,就不可再更改

    下面演示一下RS

    假设我们有一份联系人名单,每个联系人之间用空行分割,格式如下

      $ cat contacts 
    Name1
    Addr1
    ZipCode1
    Tele1
    
    Name2
    Addr2
    ZipCode2
    Tele2
    
    Name3
    Addr3
    ZipCode3
    Tele3
     

    下面我们想读取姓名与电话。

    默认情况下,gawk以换行符为行分隔符,这里我们可以使用空作为行分隔符。这样做的结果就是,使得每一个联系人作为单独的“一行”。然后我们再把字段分隔符设置为换行符,这样就可以提取我们所需要的内容了。

      $ gawk 'BEGIN{FS="
    ";RS=""} {print $1,$4}' contacts 
    Name1 Tele1
    Name2 Tele2
    Name3 Tele3
     

    数据变量

    变量 描述
    ARGC 当前命令行参数个数
    ARGIND 当前文件在ARGV中的位置
    ARGV 包含命令行参数的数组
    CONVFMT 数字的转换格式(参见print语句);默认值为%.6 g
    ENVIRON 当前shell环境变量及其值组成的关联数组
    ERRNO 当读取或关闭输入文件发生错误时的系统错误号
    FILENAME 用作gawk输入数据的数据文件的文件名
    FNR 当前数据文件中的数据行数
    IGNORECASE 设成非0值时,忽略gawk命令中出现的字符串的字符大小写
    NF 数据文件中的字段总数
    NR 已处理的输入数据行数目
    OFMT 数字的输出格式;默认值为%.6 g
    RLENGTH 由match函数所匹配的子字符串的长度
    RSTART 由match函数所匹配的子字符串的起始位置

    在gawk中引用gawk变量的时候,不需要美元符号

      $ gawk 'BEGIN{print ARGC,ARGV[0],ARGV[1]}' contacts
    2 gawk contacts
     

    上面命令中共两个参数,gawk以及contacts

    注意:gawk后的脚本不算参数

    我们可以通过如下方式获取系统环境变量,既而在gawk中使用。

      $ gawk 'BEGIN{print ENVIRON["HOME"]}' 
    /home/su1216
     

    我们可以通过如下方式来指定最后一个字段

      $ gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd
    root:/bin/bash
    daemon:/bin/sh
    bin:/bin/sh
    sys:/bin/sh
    ......
     

    注意:这里gawk先把NF计算出来,然后再执行$number,而不是像shell中把$NF视为对NF变量的引用。

    下面看一下FNR与NR的区别

      $ gawk 'BEGIN{FS="
    ";RS=""} {print $1,"FNR="FNR, "NR="NR}' contacts contacts 
    Name1 FNR=1 NR=1
    Name2 FNR=2 NR=2
    Name3 FNR=3 NR=3
    Name1 FNR=1 NR=4
    Name2 FNR=2 NR=5
    Name3 FNR=3 NR=6
     

    如果只使用一个数据文件作为输入,那么FNR与NR则一致。

    处理第二个文件时,FNR变量被重置。

    自定义变量

    在脚本中给变量赋值

    同shell中的变量赋值类似:

      $ gawk '
    > BEGIN{
    > test="This is a test."
    > print test
    > test="Test again."
    > print test
    > }'
    This is a test.
    Test again.
     

    还可以在其中直接进行数学运算

      $ gawk '
    BEGIN{
    x=1                   
    print "x="x
    x=(x+1)**2
    print "x="x
    }'
    x=1
    x=4
     

    可以直接进行求余(%)和方幂(**或者^)运算

    在命令行上给变量赋值

    可以在gawk脚本之外给gawk中变量赋值

      $ cat gawk_script
    BEGIN{FS=":"}{print $n}
     

    上面是一个普通的gawk脚本,其中使用了变量n,但是并没有为其进行初始化。

      $ gawk -f gawk_script n=1 /etc/passwd
    root
    daemon
    bin
    ......
     

    但是这个变量在BEGIN中是无效的,如果想在BEGIN中生效,需要使用-v选项。

      $ gawk -v n=1 -f gawk_script /etc/passwd
     

    处理数组

    gawk使用关联数组来提供数组功能,它的索引可以是任意文本字符串。

    定义数组变量

      $ gawk '
    > BEGIN{test["a"]="a"
    > print test["a"]
    > }'
    a
     

    遍历数组变量

    可以使用for语句:

      for (var in array)
    {
        statements
    }
     

    例如:

      $ gawk '
    > BEGIN {
    > test["a"]="a1"
    > test["b"]="b2"
    > test["c"]="c3"
    > for (var in test)
    > {
    >     print var,test[var]
    > }
    > }'
    a a1
    b b2
    c c3
     

    删除数组变量

    delete array [ index ]

      $ gawk '
    > BEGIN {
    > test["a"]="a1"
    > test["b"]="b2"
    > test["c"]="c3"
    > delete test["b"]
    > for (var in test)
    > {
    >     print var,test[var]
    > }
    > }'
    a a1
    c c3
     

    使用模式

    BEGIN和END是用来在读取数据流之前或之后执行命令的特殊模式。

    正则表达式

    正则表达式必须出现在它要控制的程序脚本的左花括号前:

      $ gawk '/Name[0-9]/{print $0}' contacts 
    Name1
    Name2
    Name3
     

    匹配操作符(matching operator)

    匹配操作符允许将正则表达式限定在数据行中的特定数据字段。匹配操作符是波浪线(~)。 需要一起指定匹配操作符、数据字段变量以及正则表达式:

      $ gawk 'BEGIN{FS=":"} $1 ~ /sys/{print $0}' /etc/passwd
    sys:x:3:3:sys:/dev:/bin/sh
    syslog:x:101:103::/home/syslog:/bin/false
     

    这里$1表示第一个字段,也就是说,打印出第一个字段中含有sys字样的行

    可以使用!~来过滤没有满足正则表达式的字符串。

      $ gawk 'BEGIN{FS=":"} $1 !~ /[aeiou]/{print $0}' /etc/passwd
    sys:x:3:3:sys:/dev:/bin/sh
    sync:x:4:65534:sync:/bin:/bin/sync
    lp:x:7:7:lp:/var/spool/lpd:/bin/sh
    gdm:x:113:120:Gnome Display Manager:/var/lib/gdm:/bin/false
    sshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin
     

    数学表达式

    可以使用下面几种形式:

    x == y

    x <= y

    x < y

    x > y

    x >= y

    这里不仅局限于数值比较,也可以作用在字符串上。

      $ gawk 'BEGIN{FS=":"} $4 == 0{print $0}' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    $ gawk 'BEGIN{FS=":"} $1 == "sys"{print $0}' /etc/passwd
    sys:x:3:3:sys:/dev:/bin/sh
     

    结构化命令

    if语句

    语句格式:

      if(condition)
        statement
    
    if(condition) statement
    
    if(condition){
        statements
    }
    
    if(condition){
        statements
    }else{
        statements
    }
    
    if(condition) statement1; else statement2
     

    while语句

    语句格式:

      while(condition){
        statements
    }
     

    do-while语句

    语句格式:

      do
    {
        statements
    } while (condition)
     

    for语句

    gawk支持C风格的for循环

    语句格式:

      for(variable assignment; condition; iteration process)
     

    格式化打印

    printf命令格式:

    printf " format string ", var1, var2 ...

    gawk程序会将每个格式化指定符作为命令中列出的每个变量的占位符使用。第一个格式化指定符会匹配列出的第一个变量,第二个会匹配第二个变量,依此类推。

    格式化指定符采用如下格式:

    %[ modifier ] control-letter

    其中 control-letter 指明显示什么类型数据值的单字符码,而 modifier 定义了另一个可选的格式化特性。

    格式化指定符的控制字母

    控制字母 描述
    c 将一个数作为ASCII字符显示
    d 显示一个整数值
    i 显示一个整数值(跟d一样)
    e 用科学记数法显示一个数
    f 显示一个浮点值
    g 用科学记数法或浮点数中较短的显示
    o 显示一个八进制值
    s 显示一个文本字符串
    x 显示一个十六进制值
    X 显示一个十六进制值,但用大写字符A-F

    除了控制字母外,还有3种修饰符可以用来进一步控制输出。

    width :指定了输出字段最小宽度的数字值。如果短于这个值,printf会向右对齐,并用空格来填充这段空间。如果输出比指定的宽度还要长,它就覆盖width值。

    prec :指定了浮点数中小数点后面位数的数字值,或者文本字符串中显示的最大字符数。

    - (减号):向格式化空间中放入数据时采用左对齐。

    printf每打印一次,不会自动换行。

      $ cat numbers 
    1 2 3
    123 456 789
    172 245 946
    
    $ gawk '{
    > total = 0
    > for (i = 1; i < 4; i++)
    > {
    >     total += $i
    > }
    > avg = total/3
    > printf "Average: %5.1f
    ",avg
    > }' numbers
    Average:   2.0
    Average: 456.0
    Average: 454.3
     

    上面指定了显示浮点数,输出最小宽度是5,保留小数后一位,右对齐。

    内建函数

    gawk数学函数

    函数 描述
    atan2(x,y) x/y的反正切,x和y以弧度为单位
    cos(x) x的余弦,x以弧度为单位
    exp(x) x的指数函数
    int(x) x的整数部分,取靠近0一侧的值
    log(x) x的自然对数
    rand() 比0大比1小的随机浮点值
    sin(x) x的正弦,x以弧度为单位
    sqrt(x) x的平方根
    srand(x) 为计算随机数指定一个种子值

    除了标准函数外,gawk还支持一些按位操作数据的函数

    and(v1, v2) :v1和v2的按位与运算

    compl(val) :val的补运算

    lshift(val, count) :将值val左移count位

    or(v1, v2) :v1和v2的按位或运算

    rshift(val, count) :将值val右移count位

    xor(v1, v2) :执行值v1和v2的按位异或运算

    gawk字符串函数

    函数 描述
    asort(s [, d]) 将数组s按数据元素值排序。索引值会被替换成表示新的排序顺序的连续数字。如果指定了d,则排序后的数组回存储在数组d中
    asorti(s [, d]) 将数组s按索引值排序。生成的数组会将索引值作为数据元素值,用连续数组索引来表明排序顺序。如果指定了d,排序后的数组回存储在数组d中
    gensub(r, s, h [, t]) 查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果h是一个以g或者G开头的字符串,就用s替换掉匹配的文本。如果h是一个数字,它表示要替换掉第几处r匹配的地方
    gsub(r, s, [, t]) 查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果找到了,就全部替换成字符串s
    index(s, t) 返回字符串t在字符串s中的索引值,如果没找到的话,返回0
    length([s]) 返回字符串s的长度,如果没有指定的话,返回$0的长度
    match(s, r [, a]) 返回字符串s中正则表达式r出现位置的索引。如果指定了数组a,它会存储s中匹配正则表达式的那部分
    split(s, a [, r]) 将s用FS字符或正则表达式r(如果指定了的话),分开放到数组a中。返回字段的总数
    sprintf(format, variables) 用提供的format和variables返回一个类似于printf输出的字符串
    sub(r, s [, t]) 在变量$0或目标字符串t中查找正则表达式r的匹配。如果找到了,就用字符串s替换掉第一处匹配
    substr(s, i [, n]) 返回s中从索引值i开始的n个字符组成的子字符串。如果未提供n,则返回s剩下的部分
    tolower(s) 将s的所有字符转换成小写
    toupper(s) 将s的所有字符转换成大写

    gawk的时间函数

    函数 描述
    mktime(datespec) 将一个YYYY MM DD HH MM SS [DST]格式置顶的日期转换彻骨时间戳(时间戳指:自1970-01-01 00:00:00 UTC到现在,以秒为单位的计数,通常称epoch time)
    strftime(format [, timestamp]) 将当前时间的时间戳huotimestamp(如果提供了的话)转化成用shell函数格式date()的格式化日期
    systime() 返回当前时间的时间戳(同上面的时间戳)

    自定义函数

    定义函数

    必须使用function关键字

      function name([variables])
    {
        statements
    }
     

    可以使用return语句返回值

    使用自定义函数

    定义函数时,必须在所有代码块之前,包括BEGIN

      $ gawk '
    > function test(v1){
    >     print v1
    > }
    > BEGIN{
    > test("abc")
    > }
    > '
    abc
     

    使用函数库

    1.首先需要创建一个存储gawk函数的文件:

      $ cat gawk_lib
    function test(v1){
        print v1
    }
    
    function add(){
        print $1+$2+$3
    }
     

    2.然后通过使用-f选项来指定函数库即可:

      $ cat gawk_lib_test
    BEGIN{
        FS=" "
    }
    
    {
        add()
    }
    
    
    $ gawk -f gawk_lib -f gawk_lib_test numbers 
    6
    1368
    10363
     

    转贴请保留以下链接

    本人blog地址

    http://su1216.iteye.com/

    http://blog.csdn.net/su1216/