• 【转载】GAWK AWK工具使用手册


    IBM GAWK入门资料http://www.ibm.com/developerworks/cn/education/aix/au-gawk/

    AWK 是什么?

    最简单地说,AWK 是一种用于处理文本的编程语言工具。AWK 实用工具的语言在很多方面类似于 shell 编程语言,尽管 AWK 具有完全属于其本身的语法。在最初创造 AWK 时,其目的是用于文本处理,并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。该实用工具扫描文件中的每一行,查找与命令行中所给定 内容相匹配的模式。如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行。

    您正在学习 Linux 吗?本文对于非常有用的 AWK 文本操作工具进行了介绍,非常有价值。

    AWK 实用工具带有其自己的自包含语言,它不仅是 Linux 中也是任何环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称得自于它的创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母)的最大功能取决于一个人所拥有的知识。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报 表,还有无数其他的功能。

    gawk 手册

     GAWK

    第一章 前言
    第二章 简介
    第三章 读取输入档案
    第四章 印出
    第五章 Patterns
    第六章 算式(Expression)作为Actions的叙述
    第七章 Actions里面的控制叙述
    第八章 内建函式(Built-in Functions)
    第九章 使用者定义的函式
    第十章 □例
    第十一章 结论


    =======================================

    第一章 前言

    awk 是一个程式语言,对於资料的处理具有很强的功能。对於文
    字档里的资料做修改、比对、抽取等的处理,awk 能够以很短的程式
    轻易地完成。如果使用 C 或 Pascal 等语言写程式完成上述的动作,
    会不方便且很花费时间,所写的程式也会很大。

    awk 能够依照使用者的定义格式来分解输入资料,也可依照使用
    者定义的格式来印出资料。

    awk 名称的由来是由它的原始设计者的姓氏之第一个字母而命名
    :Alfred V. Aho, Peter J. Weinberger, Brian W. Kernighan。
    awk最初在1977年完成。一个新版本的awk在1985年被发表,它的功能
    比旧版本增强不少。

    gawk 是GNU所做的 awk,gawk 最初在1986年完成,之後不断地
    被改进、更新。gawk 包含 awk 的所有功能。

    往後的 gawk 将以下面的2个输入档案来做例子说明。

    档案'BBS-list':
    aardvark 555-5553 1200/300 B
    alpo-net 555-3412 2400/1200/300 A
    barfly 555-7685 1200/300 A
    bites 555-1675 2400/1200/300 A
    camelot 555-0542 300 C
    core 555-2912 1200/300 C
    fooey 555-1234 2400/1200/300 B
    foot 555-6699 1200/300 B
    macfoo 555-6480 1200/300 A
    sdace 555-3430 2400/1200/300 A
    sabafoo 555-2127 1200/300 C

    档案'shipped':
    Jan 13 25 15 115
    Feb 15 32 24 226
    Mar 15 24 34 228
    Apr 31 52 63 420
    May 16 34 29 208
    Jun 31 42 75 492
    Jul 24 34 67 436
    Aug 15 34 47 316
    Sep 13 55 37 277
    Oct 29 54 68 525
    Nov 20 87 82 577
    Dec 17 35 61 401

    Jan 21 36 64 620
    Feb 26 58 80 652
    Mar 24 75 70 495
    Apr 21 70 74 514


    第二章 简介

    gawk 的主要功能是针对档案的每一行(line)搜寻指定的 patterns
    。当一行里有符合指定的 patterns,gawk 就会在此一行执行被指定
    的 actions。 gawk 依此方式处理输入档案的每一行直到输入档案结
    束。

    gawk 程式是由很多的 pattern 与 action 所组成,action 写在
    大括号 { } 里面,一个pattern後面就跟著一个action。整个 gawk 程
    式会像下面的样子:

    pattern {action}
    pattern {action}

    在 gawk 程式里面的规则,pattern 或 action 能够被省略,但
    是两个不能同时被省略。如果 pattern 被省略,对於输入档里面的
    每一行,action 都会被执行。如果 action 被省略,内定的 action
    则会印出所有符合 pattern 的输入行。


    2.1 如何执行gawk程式

    基本上,有2个方法可以执行gawk程式。

    □如果 gawk 程式很短,则 gawk 可以直接写在 command line,如下所示:

    gawk 'program' input-file1 input-file2 ...

    其中 program 包括一些 pattern 和 action。

    □如果 gawk 程式较长,较为方便的做法是将 gawk 程式存在一个档案,
    即 patterns 与 actions 写在档名为 program-file 的档案里面,执行
    gawk 的格式如下所示:

    gawk -f program-file input-file1 input-file2 ...

    gawk 程式的档案不止一个时,执行gawk 的格式如下所示:

    gawk -f program-file1 -f program-file2 ... input-file1
    input-file2 ...


    2.2 一个简单的例子

    现在我们举一个简单的例子,因为 gawk 程式很短,所以将 gawk 程
    式直接写在 command line。

    gawk '/foo/ {print $0}' BBS-list

    实际的 gawk 程式为 /foo/ {print $0}。/foo/ 为 pattern,意思为搜
    寻输入档里的每一行是否含有子字串 'foo',如果含有 'foo' 则执行 action。
    action 为 print $0,是将现在这一行的内容印出。BBS-list 是输入的档案。

    执行完上述指令後,会印出下面的结果:
    fooey 555-1234 2400/1200/300 B
    foot 555-6699 1200/300 B
    macfoo 555-6480 1200/300 A
    sabafoo 555-2127 1200/300 C


    2.3 一个较复杂的例子

    gawk '$1 == "Feb" {sum=$2+$3} END {print sum}' shipped

    现在这个例子会将输入档 'shipped' 的第一个栏位与 "Feb" 做比较
    ,如果相等,则其对应的第2栏位与第3栏位的值会被加到变数 sum。
    对於输入档的每一行重复上述的动作,直到输入档的每一行都被处理
    过为止。最後将 sum 的值印出。END {print sum} 的意思为在所有的输
    入读完之後,执行一次 print sum 的动作,也就是把 sum 的值印出。

    下面是执行的结果:
    84


    第三章 读取输入档案

    gawk的输入可以从标准输入或指定的档案里读取。输入的读取单
    位被称为”记录”(records),gawk 在做处理时,是一个记录一个记
    录地处理。每个记录的内定值是一行(line),一个记录又被分为多个
    栏位(fields)。


    3.1 如何将输入分解成记录(records)

    gawk 语言会把输入分解成记录(record)。记录与记录之间是以
    record separator 隔开,record separator 的内定值是表示新一行的
    字元(newline character),因此内定的 record separator 使得文字
    的每一行是一个记录。

    record separator 随著内建变数 RS 的改变而改变。RS 是一个字串,
    它的内定值是" "。仅有 RS 的第一个字元是有效的,它被当作 record
    separator,而 RS 的其它字元会被忽略。

    内建变数 FNR 会储存目前的输入档案已经被读取的记录之个数。内
    建变数 NR 会储存目前为止所有的输入档案已经被读取的记录之个数。


    3.2 栏位(field)

    gawk 会自动将每个记录分解成多个栏位 (field)。类似於字在一
    行里面,gawk 的内定动作会认为栏位之间是以 whitespace 分开。在
    gawk 里,whitespace 的意思是一个或多个空白或 tabs。

    在 gawk 程式里面,以'$1'表示第一个栏位,'$2'表示第二个栏位
    ,依此类推。举个例子,假设输入的一行如下所示:

    This seems like a pretty nice example.

    第一个栏位或 $1 是'This',第二个栏位或 $2 是 'seems',依此类推。
    有个地方值得特别注意,第七个栏位或 $7 是'example.'而非'example'。

    不论有多少栏位,$NF 可用来表示一个记录的最後一个栏位。以
    上面的例子为例,$NF 与 $7 相同,也就是'example.'。

    NF 是一个内建变数,它的值表示目前这个记录之栏位的个数。

    $0,看起来好像是第零个栏位,它是一个特例,它表示整个记录。

    下面是一个较复杂的例子:

    gawk '$1~/foo/ {print $0}' BBS-list

    结果如下:
    fooey 555-1234 2400/1200/300 B
    foot 555-6699 1200/300 B
    macfoo 555-6480 1200/300 A
    sabafoo 555-2127 1200/300 C

    这个例子是把输入档'BBS-list'的每个记录的第一个栏位作检查,如
    果它含有子字串'foo',则这一个记录会被印出。


    3.3 如何将记录分解成栏位

    gawk 根据 field separator 将一个记录分解成栏位。field sepa-
    rator 以内建变数 FS 表示。

    举个例子,假如 field separator 是'oo',则下面的行:

    moo goo gai pan

    会被分成三个栏位:'m'、' g'、' gai pan'。

    在 gawk 程式里,可以使用'='来改变 FS 的值。例如:

    gawk 'BEGIN {FS=","}; {print $2}'

    输入行如下:

    John Q. Smith, 29 Oak St., Walamazoo, MI 42139

    执行gawk的结果将印出字串 ' 29 Oak St.'。BEGIN 後面的 action 会在
    第一个记录被读取之前执行一次。


    第四章 印出

    在gawk程式里,actions 最常做的事就是印出(printing)。简单
    的印出,使用 printe叙述。复杂格式的印出,使用 printf 叙述。


    4.1 print叙述

    print 叙述用在简单、标准的输出格式。叙述的格式如下所示:

    print item1, item2, ...

    输出时,各个 item 之间会以一个空白分开,最後会换行(newline)。

    如果 'print'叙述之後没有跟著任何东西,它与'print $0'的效
    果一样,它会印出现在的记录(record)。要印出空白行可使用'print
    ""'。 印出一段固定的文字,可用双引号将文字的两边括起来,例如
    'print "Hello there"'。

    这里是一个例子,它会把每个输入记录的前二个栏位印出:

    gawk '{print $1,$2}' shipped

    结果如下所示:
    Jan 13
    Feb 15
    Mar 15
    Apr 31
    May 16
    Jun 31
    Jul 24
    Aug 15
    Sep 13
    Oct 29
    Nov 20
    Dec 17

    Jan 21
    Feb 26
    Mar 24
    Apr 21


    4.2 Output Separators

    前面我们已提过如果 print 叙述包含有多个 item,item 之间
    用逗点分开,则印出时各个item会被一个空白隔开。你能够使用任何
    的字串作为 output field separator,可以经由内建变数 OFS 的设
    定来更改 output field separator。OFS 的初始值为" ",即一格的
    空白。

    整个 print 叙述的输出被称为 output record。print 叙述输
    出 output record 之後,会接著输出一个字串,此字串称为 output
    record separator。内建变数 ORS 用来指明此字串。ORS 的初始值
    为 " ",也就是换行。

    下面这个例子会印出每个记录的第一个栏位和第二个栏位,此二
    个栏位之间以分号';'分开,每行输出之後会加入一个空白行。

    gawk 'BEGIN {OFS=";"; ORS=" "} {print $1, $2}' BBS-list

    结果如下所示:
    aardvark;555-5553

    alpo-net;555-3412

    barfly;555-7685

    bites;555-1675

    camelot;555-0542

    core;555-2912

    fooey;555-1234

    foot;555-6699

    macfoo;555-6480

    sdace;555-3430

    sabafoo;555-2127

    4.3 printf叙述

    printf 叙述会使得输出格式较容易精确地控制。printf 叙述可以
    指定每个 item 印出的宽度,也可以指定数字的各种型式。

    printf 叙述的格式如下:

    printf format, item1, item2, ...

    print 与 printf 的差别是在於 format, printf 的引数比 print
    多了字串 format。format 的型式与 ANSI C 的 printf 之格式相同。

    printf 并不会做自动换行的动作。内建变数 OFS 与 ORS 对 printf 叙
    述没有任何影响。

    格式的指定以字元'%'开始,後面接著格式控制字母。

    格式控制字母如下所示:

    'c' 将数字以 ASCII 字元印出。
    例如'printf "%C",65'会印出字元'A'。

    'd' 印出十进位的整数。

    'i' 印出十进位的整数。

    'e' 将数字以科学符号的形式印出。
    例如

    print "$4.3e",1950

    结果会印出'1.950e+03'。

    'f' 将数字以浮点的形式印出。

    'g' 将数字以科学符号的形式或浮点的形式印出。数字的绝对值如果
    大於等於0.0001则以浮点的形式印出,否则以科学符号的形式印
    出。

    'o' 印出无号的八进位整数。

    's' 印出一个字串。

    'x' 印出无号的十六进位整数。10至15以'a'至'f'表示。

    'X' 印出无号的十六进位整数。10至15以'A'至'F"表示。

    '%' 它并不是真正的格式控制字母,'%%"将印出"%'。

    在 % 与格式控制字母之间可加入 modifier,modifier 是用来进一
    步控制输出的格式。可能的 modifier 如下所示:

    '-' 使用在 width 之前,指明是向左靠齐。如果'-'没有出现,则会在
    被指定的宽度向右靠齐。例如:

    printf "%-4S", "foo"

    会印出'foo '。

    'width' 这一个数字指示相对应的栏位印出时的宽度。例如:

    printf "%4s","foo"

    会印出' foo'。

    width 的值是一个最小宽度而非最大宽度。如果一个 item 的
    值需要的宽度比 width 大,则不受 width 的影响。例如

    printf "%4s","foobar"

    将印出'foobar'。

    '.prec' 此数字指定印出时的精确度。它指定小数点右边的位数。如
    果是要印出一个字串,它指定此字串最多会被印出多少个字
    元。


    第五章 patterns

    在 gawk 程式里面,当 pattern 符合现在的输入记录(record),其
    相对应的 action 才会被执行。


    5.1 Pattern的种类

    这里对 gawk 的各种 pattern 型式作一整理:

    /regular expression/
    一个 regular expression 当作一个 pattern。每当输入记录 (
    record)含有 regular expression 就视为符合。

    expression
    一个单一的 expression。当一个值不为 0 或一个字串不是空的,
    则可视为符合。

    pat1,pat2
    一对的 patterns 以逗号分开,指定记录的□围。

    BEGIN
    END
    这是特别的 pattern, gawk 在开始执行或要结束时会分别执行相
    对应於BEGIN或END的 action。

    null
    这是一个空的pattern,对於每个输入记录皆视为符合pattern。


    5.2 Regular Expressions当作Patterns

    一个 regular expression 可简写为 regexp,是一种描述字串的方
    法。一个 regular expression 以斜线('/')包围当作 gawk 的 pattern。

    如果输入记录含有 regexp 就视为符合。例如:pattern 为 /foo/,
    对於任何输入记录含有'foo'则视为符合。

    下面的例子会将含有'foo'的输入记录之第2个栏位印出。

    gawk '/foo/ {print $2}' BBS-list

    结果如下:
    555-1234
    555-6699
    555-6480
    555-2127

    regexp 也能使用在比较的算式。

    exp ~ /regexp/
    如果 exp 符合 regexp,则结果为真(true)。

    exp !~ /regexp/
    如果 exp 不符合 regexp,则结果为真。


    5.3 比较的算式当作Patterns

    比较的 pattern 用来测试两个数字或字串的关系诸如大於、等於
    、小於。下面列出一些比较的pattern:

    x<y 如果 x 小於 y,则结果为真。
    x<=y 如果 x 小於、等於 y,则结果为真。
    x>y 如果 x 大於 y,则结果为真。
    x>=y 如果 x 大於、等於 y,则结果为真。
    x==y 如果 x 等於 y,则结果为真。
    x!=y 如果 x 不等於 y,则结果为真。
    x~y 如果 x 符合 regular expression y,则结果为真。
    x!~y 如果 x 不符合 regular expression y,则结果为真。

    上面所提到的 x 与 y,如果二者皆是数字则视为数字之间的比较,
    否则它们会被转换成字串且以字串的形式做比较。两个字串的比较,
    会先比较第一个字元,然後比较第二个字元,依此类推,直到有不同
    的地方出现为止。如果两个字串在较短的一个结束之前是相等,则视
    为长的字串比短的字串大。例如 "10" 比 "9" 小,"abc" 比 "abcd" 小。


    5.4 使用布林运算的Patterns

    一个布林(boolean) pattern 是使用布林运算"或"('||'),"及"
    ('&&'),"反"('!')来组合其它的pattern。
    例如:

    gawk '/2400/ && /foo/' BBS-list
    gawk '/2400/ || /foo/' BBS-list
    gawk '! /foo/' BBS-list


    第六章 算式(Expression)作为Actions的叙述

    算式(Expression) 是gawk程式里面action的基本构成者。


    6.1 算术运算

    gawk 里的算术运算如下所示:

    x+y 加
    x-y 减
    -x 负
    +x 正。实际上没有任何影响。
    x*y 乘
    x/y 除
    x%y 求馀数。例如 5%3=2。
    x^y
    x**y x 的 y 次方。例如2^3=8。


    6.2 比较算式与布林算式

    比较算式 (comparison expression) 用来比较字串或数字的关系
    ,运算符号与 C 语言相同。表列如下:

    x<y
    x<=y
    x>y
    x>=y
    x==y
    x!=y
    x~y
    x!~y

    比较的结果为真(true)则其值是 1。否则其值是 0。

    布林算式(boolean expression)有下面三种:

    boolean1 && boolean2
    boolean1 || boolean2
    ! boolean


    6.3 条件算式(Conditional Expressions)

    一个条件式算式是一种特别的算式,它含有3个运算元。
    条件式算式与C语言的相同:

    selector ? if-true-exp : if-false-exp

    它有3个子算式。第一个子算式selector 首先会被计算。如果是真,
    则if-true-exp会被计算且它的值变成整个算式的值。否则if-false-
    exp 会被计算且它的值变成整个算式的值。

    例如下面的例子会产生x的绝对值:

    x>0 ? x : -x


    第七章 Actions里面的控制叙述

    在 gawk 程式里面,控制叙述诸如 if、while 等控制程式执行的流
    程。在 gawk 里的控制叙述与 C 的类似。

    很多的控制叙述会包括其它的叙述,被包括的叙述称为 body。假
    如 body 里面包括一个以上的叙述,必须以大括弧 { } 将这些叙述括起
    来,而各个叙述之间需以换行(newline)或分号隔开。


    7.1 if 叙述

    if (condition) then-body [else else-body]

    如果 condition 为真(true),则执行 then-body,否则执行 else-body。

    举一个例子如下:

    if (x % 2 == 0)
    print "x is even"
    else
    print "x is odd"


    7.2 while 叙述

    while (condition)
    body

    while 叙述做的第一件事就是测试 condition。假如 condition 为真则
    执行 body 的叙述。body 的叙述执行完後,会再测试 condition,假如
    condition 为真,则 body 会再度被执行。这个过程会一直被重复直到
    condition 不再是真。如果 condition 第一次测试就是伪(false),则
    body 从没有被执行。

    下面的例子会印出每个输入记录(record)的前三个栏位。

    gawk '{ i=1
    while (i <= 3) {
    print $i
    i++
    }
    }'


    7.3 do-while 叙述

    do
    body
    while (condition)

    这个 do loop 执行 body 一次,然後只要 condition 是真则会重复执行 body。
    即使开始时 condition 是伪,body 也会被执行一次。

    下面的例子会印出每个输入记录十次。

    gawk '{ i= 1
    do {
    print $0
    i++
    } while (i <= 10)
    }'


    7.4 for 叙述

    for (initialization; condition; increment)
    body

    此叙述开始时会执行initialization,然後只要 condition是真,它
    会重复执行body与做increment 。

    下面的例子会印出每个输入记录的前三个栏位。

    gawk '{ for (i=1; i<=3; i++)
    print $i
    }'


    7.5 break 叙述

    break 叙述会跳出包含它的 for、while、do-while 回圈的最内层。

    下面的例子会找出任何整数的最小除数,它也会判断是否为质数。

    gawk '# find smallest divisor of num
    { num=$1
    for (div=2; div*div <=num; div++)
    if (num % div == 0)
    break
    if (num % div == 0)
    printf "Smallest divisor of %d is %d ", num, div
    else
    printf "%d is prime ", num }'


    7.6 continue 叙述

    continue 叙述使用於 for、while、do-while 回圈内部,它会跳
    过回圈 body 的剩馀部分,使得它立刻进行下一次回圈的执行。

    下面的例子会印出 0 至 20 的全部数字,但是 5 并不会被印出。

    gawk 'BEGIN {
    for (x=0; x<=20; x++) {
    if (x==5)
    continue
    printf ("%d",x)
    }
    print ""
    }'


    7.7 next 叙述、next file 叙述、exit 叙述

    next 叙述强迫 gawk 立刻停止处理目前的记录(record)而继续下一
    个记录。

    next file 叙述类似 next。然而,它强迫 gawk 立刻停止处理目前
    的资料档。

    exit 叙述会使得 gawk 程式停止执行而跳出。然而,如果 END 出现
    ,它会去执行 END 的 actions。


    第八章 内建函式(Built-in Functions)

    内建函式是 gawk 内建的函式,可在 gawk 程式的任何地方呼叫内建
    函式。


    8.1 数值方面的内建函式

    int(x) 求出 x 的整数部份,朝向 0 的方向做舍去。例如:int(3.9)
    是 3,int(-3.9) 是 -3。
    sqrt(x) 求出 x 正的平方根值。例 sqrt(4)=2
    exp(x) 求出 x 的次方。例 exp(2) 即是求 e*e 。
    log(x) 求出 x 的自然对数。
    sin(x) 求出 x 的 sine 值,x 是弪度量。
    cos(x) 求出 x 的 cosine 值,x 是弪度量。
    atan2(y,x) 求 y/x 的 arctangent 值,所求出的值其单位是弪度量。
    rand() 得出一个乱数值。此乱数值平均分布在 0 和 1 之间。这个
    值不会是 0,也不会是 1。
    每次执行 gawk,rand 开始产生数字从相同点或 seed。
    srand(x) 设定产生乱数的开始点或 seed 为 x。如果在第二次你设
    定相同的 seed 值,你将再度得到相同序列的乱数值。
    如果省略引数 x,例如 srand(),则现在的日期、时间会
    被当成 seed。这个方法可使得乱数值是真正不可预测的。
    srand 的传回值(return value)是前次所设定的 seed 值。


    8.2 字串方面的内建函式

    index(in, find)
    它会在字串 in 里面,寻找字串 find 第一次出现的地方,传回值是
    字串 find 出现在字串 in 里面的位置。如果在字串 in 里面找不到字
    串 find,则传回值为 0。
    例如:
    print index("peanut","an")
    会印出 3。

    length(string)
    求出 string 有几个字元。
    例如:
    length("abcde")
    是 5。

    match(string,regexp)
    match 函式会在字串 string 里面,寻找符合 regexp 的最长、最靠
    左边的子字串。传回值是 regexp 在 string 的开始位置,即 index
    值。
    match 函式会设定内在变数 RSTART 等於 index,它也会设定内在变
    数 RLENGTH 等於符合的字元个数。如果不符合,则会设定 RSTART 为
    0、RLENGTH 为 -1。

    sprintf(format,expression1,...)
    举 printf 类似,但是 sprintf 并不印出,而是传回字串。
    例如:
    sprintf("pi = %.2f (approx.)',22/7)
    传回的字串为"pi = 3.14 (approx.)"

    sub(regexp, replacement,target)
    在字串 target 里面,寻找符合 regexp 的最长、最靠左边的地方,
    以字串 replacement 代替最左边的 regexp。
    例如:
    str = "water, water, everywhere"
    sub(/at/, "ith",str)
    结果字串str会变成
    "wither, water, everywhere"

    gsub(regexp, replacement, target)
    gsub 与前面的 sub 类似。在字串 target 里面,寻找符合 regexp 的
    所有地方,以字串 replacement 代替所有的 regexp。
    例如:
    str="water, water, everywhere"
    gsub(/at/, "ith",str)
    结果字串str会变成
    'wither, wither, everywhere"

    substr(string, start, length)
    传回字串 string 的子字串,这个子字串的长度为 length 个字元,
    从第 start 个位置开始。
    例如:
    substr("washington",5,3)
    传回值为"ing"
    如果 length 没有出现,则传回的子字串是从第 start 个位置开始
    至结束。
    例如:
    substr("washington",5)
    传回值为"ington"

    tolower(string)
    将字串string的大写字母改为小写字母。
    例如:
    tolower("MiXeD cAsE 123")
    传回值为"mixed case 123"

    toupper(string)
    将字串string的小写字母改为大写字母。
    例如:
    toupper("MiXeD cAsE 123")
    传回值为"MIXED CASE 123"


    8.3 输入输出的内建函式

    close(filename)
    将输入或输出的档案 filename 关闭。

    system(command)
    此函式允许使用者执行作业系统的指令,执行完毕後将回到 gawk
    程式。
    例如:
    BEGIN {system("ls")}


    第九章 使用者定义的函式(User-defined Functions)

    复杂的 gawk 程式常常可以使用自己定义的函式来简化。呼叫使用
    者定义的函式与呼叫内建函式的方法一样。


    9.1 函式定义的格式

    函式的定义可以放在 gawk 程式的任何地方。

    一个使用者定义的函式其格式如下:

    function name (parameter-list) {
    body-of-function
    }

    name 是所定义的函式之名称。一个正确的函式名称可包括一序列的字
    母、数字、下标线 (underscores),但是不可用数字做开头。

    parameter-list 是列出函式的全部引数(argument),各个引数之
    间以逗点隔开。

    body-of-function 包含 gawk 的叙述 (statement)。它是函式定义
    里最重要的部份,它决定函式实际要做何种事。


    9.2 函式定义的例子

    下面这个例子,会将每个记录的第一个栏位之值的平方与第二个
    栏位之值的平方加起来。

    {print "sum =",SquareSum($1,$2)}
    function SquareSum(x,y) {
    sum=x*x+y*y
    return sum
    }


    第十章 □例

    这里将列出 gawk 程式的一些例子。

    gawk '{if (NF > max) max = NF}
    END {print max}'
    此程式会印出所有输入行之中,栏位的最大个数。

    gawk 'length($0) > 80'
    此程式会印出一行超过 80 个字元的每一行。此处只有 pattern 被
    列出,action 是采用内定的 print。

    gawk 'NF > 0'
    对於拥有至少一个栏位的所有行,此程式皆会印出。这是一个简
    单的方法,将一个档案里的所有空白行删除。

    gawk '{if (NF > 0) print}'
    对於拥有至少一个栏位的所有行,此程式皆会印出。这是一个简
    单的方法,将一个档案里的所有空白行删除。

    gawk 'BEGIN {for (i = 1; i <= 7; i++)
    print int(101 * rand())}'
    此程式会印出□围是 0 到 100 之间的 7 个乱数值。

    ls -l files | gawk '{x += $4}; END {print "total bytes: " x}'
    此程式会印出所有指定的档案之bytes数目的总和。

    expand file | gawk '{if (x < length()) x = length()}
    END {print "maximum line length is " x}'
    此程式会将指定档案里最长一行的长度印出。expand 会将 tab 改
    成 space,所以是用实际的右边界来做长度的比较。

    gawk 'BEGIN {FS = ":"}
    {print $1 | "sort"}' /etc/passwd
    此程式会将所有使用者的login名称,依照字母的顺序印出。

    gawk '{nlines++}
    END {print nlines}'
    此程式会将一个档案的总行数印出。

    gawk 'END {print NR}'
    此程式也会将一个档案的总行数印出,但是计算行数的工作由gawk
    来做。

    gawk '{print NR,$0}'
    此程式印出档案的内容时,会在每行的最前面印出行号,它的功
    能与 'cat -n' 类似。


    第十一章 结论

    gawk 对於资料的处理具有很强的功能。它能够以很短的程式完成
    想要做的事,甚至一或二行的程式就能完成指定的工作。同样的一件
    工作,以 gawk 程式来写会比用其它程式语言来写短很多。

    gawk 是 GNU 所做的 awk,它是公众软体(Public Domain) 可免费使
    用。
     

    ====================================AWK 手册(版本二)==============================================

    AWK:Linux 管理员的智能工具包

    您正在学习 Linux 吗?本文对于非常有用的 AWK 文本操作工具进行了介绍,非常有价值。

    AWK 实用工具带有其自己的自包含语言,它不仅是 Linux 中也是任何环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称得自于它的创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母)的最大功能取决于一个人所拥有的知识。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报 表,还有无数其他的功能。

    AWK 是什么?

    最简单地说,AWK 是一种用于处理文本的编程语言工具。AWK 实用工具的语言在很多方面类似于 shell 编程语言,尽管 AWK 具有完全属于其本身的语法。在最初创造 AWK 时,其目的是用于文本处理,并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。该实用工具扫描文件中的每一行,查找与命令行中所给定 内容相匹配的模式。如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行。

    尽管操作可能会很复杂,但命令的语法始终是:


    awk '{pattern + action}' {filenames}


    其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号 ({}) 不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。

    了解字段

    实用工具将每个输入行分为记录和字段。记录是单行的输入,而每条记录包含若干字段。默认的字段分隔符是空格或制表符,而记录的分隔符是换行。虽然在默认情况下将制表符和空格都看作字段分隔符(多个空格仍然作为一个分隔符),但是可以将分隔符从空格改为任何其它字符。

    为了进行演示,请查看以下保存为 emp_names 的员工列表文件:


    46012   DULANEY     EVAN        MOBILE   AL
    46013   DURHAM      JEFF        MOBILE   AL
    46015   STEEN       BILL        MOBILE   AL
    46017   FELDMAN     EVAN        MOBILE   AL
    46018   SWIM        STEVE       UNKNOWN  AL
    46019   BOGUE       ROBERT      PHOENIX  AZ
    46021   JUNE        MICAH       PHOENIX  AZ
    46022   KANE        SHERYL      UNKNOWN  AR
    46024   WOOD        WILLIAM     MUNCIE   IN
    46026   FERGUS      SARAH       MUNCIE   IN
    46027   BUCK        SARAH       MUNCIE   IN
    46029   TUTTLE      BOB         MUNCIE   IN


    当 AWK 读取输入内容时,整条记录被分配给变量 $0。每个字段以字段分隔符分开,被分配给变量 $1、$2、$3 等等。一行在本质上可以包含无数个字段,通过字段号来访问每个字段。因此,命令


    awk '{print $1,$2,$3,$4,$5}' names


    将会产生的打印输出是


    46012 DULANEY EVAN MOBILE AL
    46013 DURHAM JEFF MOBILE AL
    46015 STEEN BILL MOBILE AL
    46017 FELDMAN EVAN MOBILE AL
    46018 SWIM STEVE UNKNOWN AL
    46019 BOGUE ROBERT PHOENIX AZ
    46021 JUNE MICAH PHOENIX AZ
    46022 KANE SHERYL UNKNOWN AR
    46024 WOOD WILLIAM MUNCIE IN
    46026 FERGUS SARAH MUNCIE IN
    46027 BUCK SARAH MUNCIE IN
    46029 TUTTLE BOB MUNCIE IN


    值得注意的一项重要内容是,AWK 解释由空格分隔的五个字段,但当它打印显示内容时,在每个字段间只有一个空格。利用为每个字段指定了唯一号码的功能,您可以选择只打印特定的字段。例如,只打印每条记录的姓名时,只需选择第二个和第三个字段进行打印:


    $ awk '{print $2,$3}' emp_names
    DULANEY EVAN
    DURHAM JEFF
    STEEN BILL
    FELDMAN EVAN
    SWIM STEVE
    BOGUE ROBERT
    JUNE MICAH
    KANE SHERYL
    WOOD WILLIAM
    FERGUS SARAH
    BUCK SARAH
    TUTTLE BOB
    $


    您还可以指定按任何顺序打印字段,而无论它们在记录中是如何存在的。因此,只需要显示姓名字段,并且使其顺序颠倒,先显示名字再显示姓氏:


    $ awk '{print $3,$2}' emp_names
    EVAN DULANEY
    JEFF DURHAM
    BILL STEEN
    EVAN FELDMAN
    STEVE SWIM
    ROBERT BOGUE
    MICAH JUNE
    SHERYL KANE
    WILLIAM WOOD
    SARAH FERGUS
    SARAH BUCK
    BOB TUTTLE
    $


    使用模式

    通过包含一个必须匹配的模式,您可以选择只对特定的记录而不是所有的记录进行操作。模式匹配的最简单形式是搜索,其中要匹配的项目被包含在斜线 (/pattern/) 中。例如,只对那些居住在阿拉巴马州的员工执行前面的操作:


    $ awk '/AL/ {print $3,$2}' emp_names
    EVAN DULANEY
    JEFF DURHAM
    BILL STEEN
    EVAN FELDMAN
    STEVE SWIM
    $


    如果您不指定要打印的字段,则会打印整个匹配的条目:


    $ awk '/AL/' emp_names
    46012   DULANEY     EVAN     MOBILE     AL
    46013   DURHAM      JEFF     MOBILE     AL
    46015   STEEN       BILL     MOBILE     AL
    46017   FELDMAN     EVAN     MOBILE     AL
    46018   SWIM        STEVE    UNKNOWN    AL
    $


    对同一数据集的多个命令可以用分号 (;) 分隔开。例如,在一行中打印姓名,而在另一行中打印城市和州名:


    $ awk '/AL/ {print $3,$2 ; print $4,$5}' emp_names
    EVAN DULANEY
    MOBILE AL
    JEFF DURHAM
    MOBILE AL
    BILL STEEN
    MOBILE AL
    EVAN FELDMAN
    MOBILE AL
    STEVE SWIM
    UNKNOWN AL
    $


    如果没有使用分号 (print $3,$2,$4,$5),则会在同一行中显示所有内容。另一方面,如果分别给出两个打印语句,则会产生完全不同的结果:


    $ awk '/AL/ {print $3,$2} {print $4,$5}' emp_names
    EVAN DULANEY
    MOBILE AL
    JEFF DURHAM
    MOBILE AL
    BILL STEEN
    MOBILE AL
    EVAN FELDMAN
    MOBILE AL
    STEVE SWIM
    UNKNOWN AL
    PHOENIX AZ
    PHOENIX AZ
    UNKNOWN AR
    MUNCIE IN
    MUNCIE IN
    MUNCIE IN
    MUNCIE IN
    $


    只有在列表中找到 AL 时才会给出字段三和字段二。但是,字段四和字段五是无条件的,始终打印它们。只有第一组花括号中的命令对前面紧邻的命令 (/AL/) 起作用。

    结果非常不便于阅读,可以使其稍微更清晰一些。首先,在城市与州之间插入一个空格和逗号。然后,在每两行显示之后放置一个空行:


    $ awk '/AL/ {print $3,$2 ; print $4", "$5" "}' emp_names
    EVAN DULANEY
    MOBILE, AL

    JEFF DURHAM
    MOBILE, AL

    BILL STEEN
    MOBILE, AL

    EVAN FELDMAN
    MOBILE, AL

    STEVE SWIM
    UNKNOWN, AL
    $


    在第四和第五个字段之间,添加一个逗号和一个空格(在引号之间),在第五个字段后面,打印一个换行符 ( )。在 AWK 打印语句中还可以使用那些可在 echo 命令中使用的所有特殊字符,包括:


    (换行)
    (制表)
    (退格)
    f(进纸)
    (回车)

    因此,要读取全部五个最初由制表符分隔开的字段,并且也利用制表符打印它们,您可以编程如下


    $ awk '{print $1" "$2" "$3" "$4" "$5}' emp_names
    46012   DULANEY     EVAN     MOBILE    AL
    46013   DURHAM      JEFF     MOBILE    AL
    46015   STEEN       BILL     MOBILE    AL
    46017   FELDMAN     EVAN     MOBILE    AL
    46018   SWIM        STEVE    UNKNOWN   AL
    46019   BOGUE       ROBERT   PHOENIX   AZ
    46021   JUNE        MICAH    PHOENIX   AZ
    46022   KANE        SHERYL   UNKNOWN   AR
    46024   WOOD        WILLIAM  MUNCIE    IN
    46026   FERGUS      SARAH    MUNCIE    IN
    46027   BUCK        SARAH    MUNCIE    IN
    46029   TUTTLE      BOB      MUNCIE    IN
    $


    通过连续设置多项标准并用管道 (|) 符号将其分隔开,您可以一次搜索多个模式匹配:


    $ awk '/AL|IN/' emp_names
    46012   DULANEY     EVAN     MOBILE    AL
    46013   DURHAM      JEFF     MOBILE    AL
    46015   STEEN       BILL     MOBILE    AL
    46017   FELDMAN     EVAN     MOBILE    AL
    46018   SWIM        STEVE    UNKNOWN   AL
    46024   WOOD        WILLIAM  MUNCIE    IN
    46026   FERGUS      SARAH    MUNCIE    IN
    46027   BUCK        SARAH    MUNCIE    IN
    46029   TUTTLE      BOB      MUNCIE    IN
    $


    这样可找到每个阿拉巴马州和印第安那州居民的匹配记录。但是在试图找出居住在亚利桑那州的人时,出现了一个问题:


    $ awk '/AR/' emp_names
    46019   BOGUE       ROBERT   PHOENIX   AZ
    46021   JUNE        MICAH    PHOENIX   AZ
    46022   KANE        SHERYL   UNKNOWN   AZ
    46026   FERGUS      SARAH    MUNCIE    IN
    46027   BUCK        SARAH    MUNCIE    IN
    $


    员 工 46026 和 46027 没有住在亚利桑那州;但是他们的名字中包含所搜索的字符序列。切记,当在 AWK 中进行模式匹配时,例如 grep、sed 或者大部分其他 Linux/Unix 命令,将在记录(行)中的任何位置查找匹配,除非指定进行其他操作。为解决这一问题,必须将搜索与特定字段联系起来。通过利用代字号 (˜) 以及对特定字段的说明,可以达到这一目的,如下例所示:


    $ awk '$5 ˜ /AR/' emp_names
    46019   BOGUE       ROBERT   PHOENIX   AZ
    46021   JUNE        MICAH    PHOENIX   AZ
    46022   KANE        SHERYL   UNKNOWN   AZ
    $


    代字号(表示匹配)的对应符号是一个前面带有感叹号的代字号 (!˜)。这些字符通知程序,如果搜索序列没有出现在指定字段中,则找出与搜索序列相匹配的所有行:


    $ awk '$5 !˜ /AR/' names
    46012   DULANEY     EVAN     MOBILE    AL
    46013   DURHAM      JEFF     MOBILE    AL
    46015   STEEN       BILL     MOBILE    AL
    46017   FELDMAN     EVAN     MOBILE    AL
    46018   SWIM        STEVE    UNKNOWN   AL
    46024   WOOD        WILLIAM  MUNCIE    IN
    46026   FERGUS      SARAH    MUNCIE    IN
    46027   BUCK        SARAH    MUNCIE    IN
    46029   TUTTLE      BOB      MUNCIE    IN
    $


    在这种情况下,将显示第五个字段中没有 AR 的所有行 - 包括两个 Sarah 条目,这两个条目确实包含 AR,但却是在第三个字段而不是第五个字段中。

    花括号和字段分隔符

    括号字符在 AWK 命令中起着很重要的作用。出现在括号之间的操作指出将要发生什么以及何时发生。当只使用一对括号时:


    {print $3,$2}


    括号间的所有操作同时发生。当使用多于一对的括号时:


    {print $3}{print $2}


    执行第一组命令,在该命令完成后执行第二组命令。注意以下两列清单的区别:


    $ awk '{print $3,$2}' names
    EVAN DULANEY
    JEFF DURHAM
    BILL STEEN
    EVAN FELDMAN
    STEVE SWIM
    ROBERT BOGUE
    MICAH JUNE
    SHERYL KANE
    WILLIAM WOOD
    SARAH FERGUS
    SARAH BUCK
    BOB TUTTLE
    $

    $ awk '{print $3}{print $2}' names
    EVAN
    DULANEY
    JEFF
    DURHAM
    BILL
    STEEN
    EVAN
    FELDMAN
    STEVE
    SWIM
    ROBERT
    BOGUE
    MICAH
    JUNE
    SHERYL
    KANE
    WILLIAM
    WOOD
    SARAH
    FERGUS
    SARAH
    BUCK
    BOB
    TUTTLE
    $


    要 利用多组括号进行重复查找,执行第一组中的命令直到完成为止;然后处理第二组命令。如果有第三组命令,则在第二组命令完成后执行它,以此类推。在所生成的 打印输出中,有两个分隔的打印命令,因此先执行第一个命令,随后执行第二个命令,这样导致每个条目显示在两行而不是一行中。

    区分两个字段的字段分隔符不一定始终是空格;它可以是任何可识别的字符。为进行演示,假定 emp_names 文件利用冒号而不是制表符来分隔字段:


    $ cat emp_names
    46012:DULANEY:EVAN:MOBILE:AL
    46013:DURHAM:JEFF:MOBILE:AL
    46015:STEEN:BILL:MOBILE:AL
    46017:FELDMAN:EVAN:MOBILE:AL
    46018:SWIM:STEVE:UNKNOWN:AL
    46019:BOGUE:ROBERT:PHOENIX:AZ
    46021:JUNE:MICAH:PHOENIX:AZ
    46022:KANE:SHERYL:UNKNOWN:AR
    46024:WOOD:WILLIAM:MUNCIE:IN
    46026:FERGUS:SARAH:MUNCIE:IN
    46027:BUCK:SARAH:MUNCIE:IN
    46029:TUTTLE:BOB:MUNCIE:IN
    $


    如果试图通过指定所需要的第二个字段来打印姓氏


    $ awk '{print $2}' emp_names


    您 最后会得到十二个空行。因为文件中没有空格,除了第一个字段之外没有可认别的字段。为解决这一问题,必须通知 AWK 是空格之外的另一个字符作为分隔符,有两种方法可通知 AWK 使用新的字段分隔符:使用命令行参数 -F,或在程序中指定变量 FS。两种方法的效果相同,只有一种例外情况,如下例所示:


    $ awk '{FS=":"}{print $2}' emp_names

    DURHAM
    STEEN
    FELDMAN
    SWIM
    BOGUE
    JUNE
    KANE
    WOOD
    FERGUS
    BUCK
    TUTTLE
    $

    $ awk -F: '{print $2}' emp_names
    DULANEY
    DURHAM
    STEEN
    FELDMAN
    SWIM
    BOGUE
    JUNE
    KANE
    WOOD
    FERGUS
    BUCK
    TUTTLE
    $


    在第一个命令中,头一条记录返回不正确的空行,而其他结果正确。直到读取第二条记录时,才识别字段分隔符并正确地执行。通过使用 BEGIN 语句可以纠正这一缺点(在后文详述)。-F 的功能非常类似于 BEGIN,能够正确地读取第一条记录并按要求执行。

    在本文开始处我曾提到,默认的显示/输出字段分隔符是空格。通过使用输出字段分隔符 (OFS) 变量,可以在程序中更改此特性。例如,要读取文件(由冒号分隔)并以短划线显示,则命令是


    $ awk -F":" '{OFS="-"}{print $1,$2,$3,$4,$5}' emp_names
    46012-DULANEY-EVAN-MOBILE-AL
    46013-DURHAM-JEFF-MOBILE-AL
    46015-STEEN-BILL-MOBILE-AL
    46017-FELDMAN-EVAN-MOBILE-AL
    46018-SWIM-STEVE-UNKNOWN-AL
    46019-BOGUE-ROBERT-PHOENIX-AZ
    46021-JUNE-MICAH-PHOENIX-AZ
    46022-KANE-SHERYL-UNKNOWN-AR
    46024-WOOD-WILLIAM-MUNCIE-IN
    46026-FERGUS-SARAH-MUNCIE-IN
    46027-BUCK-SARAH-MUNCIE-IN
    46029-TUTTLE-BOB-MUNCIE-IN
    $


    FS 和 OFS 是(输入)字段分隔符和输出字段分隔符,它们只是一对可以在 AWK 实用工具中使用的变量。例如,要在打印时为每行编号,可以采用以下方式使用 NR 变量:


    $ awk -F":" '{print NR,$1,$2,$3}' emp_names
    1 46012 DULANEY EVAN
    2 46013 DURHAM JEFF
    3 46015 STEEN BILL
    4 46017 FELDMAN EVAN
    5 46018 SWIM STEVE
    6 46019 BOGUE ROBERT
    7 46021 JUNE MICAH
    8 46022 KANE SHERYL
    9 46024 WOOD WILLIAM
    10 46026 FERGUS SARAH
    11 46027 BUCK SARAH
    12 46029 TUTTLE BOB
    $


    找出员工号码处于 46012 和 46015 之间的所有行:


    $ awk -F":" '/4601[2-5]/' emp_names
    46012   DULANEY EVAN  MOBILE AL
    46013   DURHAM  JEFF  MOBILE AL
    46015   STEEN   BILL  MOBILE AL
    $


    添加文本

    可以按照添加控制序列或其他字符的相同方式将文本添加到显示中。例如,要将分隔符从空格改为冒号,则命令是


    awk '{print $1":"$2":"$3":"$4":"$5}' emp_names > new_emp_names


    在这种情况下,字符 (:) 包含在引号 ("/") 中,它被添加到每个字段之间。在引号之间的值可以是任何内容。例如,创建一个关于居住在阿拉巴马州的员工的外观类似数据库的显示:


    $ awk '$5 ~ /AL/ {print "NAME: "$2", "$3" CITY-STATE:
      "$4", "$5" "}' emp_names

    NAME: DULANEY, EVAN
    CITY-STATE: MOBILE, AL

    NAME: DURHAM, JEFF
    CITY-STATE: MOBILE, AL

    NAME: STEEN, BILL
    CITY-STATE: MOBILE, AL

    NAME: FELDMAN, EVAN
    CITY-STATE: MOBILE, AL

    NAME: SWIM, STEVE
    CITY-STATE: UNKNOWN, AL
    $


    数学操作

    AWK 除了提供文本功能,还提供全部范围的算术操作符,包括以下符号:

    + 将数字相加
    - 减
    * 乘
    / 除
    ^ 执行指数运算
    % 提供模
    ++ 将变量值加一
    += 将其他操作的结果分配给变量
    - 将变量减一
    -= 将减法操作的结果分配给变量
    *= 分配乘法操作的结果
    /= 分配除法操作的结果
    %= 分配求模操作的结果

    例如,假定您的机器上存在以下的文件,详细地列出硬件商店中的物品:


    $ cat inventory
    hammers 5       7.99
    drills  2      29.99
    punches 7       3.59
    drifts  2       4.09
    bits   55       1.19
    saws  123      14.99
    nails 800        .19
    screws 80        .29
    brads 100        .24
    $


    第一项业务定单是通过将第二个字段(数量)的值乘以第三个字段(价格)的值,计算每种物品的库存价值:


    $ awk '{print $1,"QTY: "$2,"PRICE: "$3,"TOTAL: "$2*$3}' inventory
    hammers QTY: 5 PRICE: 7.99 TOTAL: 39.95
    drills QTY: 2 PRICE: 29.99 TOTAL: 59.98
    punches QTY: 7 PRICE: 3.59 TOTAL: 25.13
    drifts QTY: 2 PRICE: 4.09 TOTAL: 8.18
    bits QTY: 55 PRICE: 1.19 TOTAL: 65.45
    saws QTY: 123 PRICE: 14.99 TOTAL: 1843.77
    nails QTY: 800 PRICE: .19 TOTAL: 152
    screws QTY: 80 PRICE: .29 TOTAL: 23.2
    brads QTY: 100 PRICE: .24 TOTAL: 24
    $


    如果这些行本身并不重要,您只是希望确定商店中有多少件物品,则可以分配一个普通变量,按照每条记录中的物品数量增加:


    $ awk '{x=x+$2} {print x}' inventory
    5
    7
    14
    16
    71
    194
    994
    1074
    1174
    $


    根据这一数据,商店中有 1174 件物品。第一次执行时,变量 x 没有值,因此它采用第一行第二个字段的值。第二次执行时,它保留了第一行的值并加上第二行的值,以此类推,直到达到累计的总合。

    可以应用相同的过程来确定现有库存的总价值:


    $ awk '{x=x+($2*$3)} {print x}' inventory
    39.95
    99.93
    125.06
    133.24
    198.69
    2042.46
    2194.46
    2217.66
    2241.66
    $


    因此,1174 件物品的价值是 $2,241.66。虽然这一过程可以获得总计值,但它的外观很差,需要加工成实际的报表。利用一些附加项,很容易使显示变得更整洁:


    $ awk '{x=x+($2*$3)}{print $1,"QTY: "$2,"PRICE: "$3,"TOTAL: "$2*$3,"BAL: "x}' inventory
    hammers QTY: 5 PRICE: 7.99 TOTAL: 39.95 BAL: 39.95
    drills QTY: 2 PRICE: 29.99 TOTAL: 59.98 BAL: 99.93
    punches QTY: 7 PRICE: 3.59 TOTAL: 25.13 BAL: 125.06
    drifts QTY: 2 PRICE: 4.09 TOTAL: 8.18 BAL: 133.24
    bits QTY: 55 PRICE: 1.19 TOTAL: 65.45 BAL: 198.69
    saws QTY: 123 PRICE: 14.99 TOTAL: 1843.77 BAL: 2042.46
    nails QTY: 800 PRICE: .19 TOTAL: 152 BAL: 2194.46
    screws QTY: 80 PRICE: .29 TOTAL: 23.2 BAL: 2217.66
    brads QTY: 100 PRICE: .24 TOTAL: 24 BAL: 2241.66
    $


    该过程提供了每条记录的清单,同时将总价值分配给库存值,并保持商店资产的运作平衡。

    BEGIN 和 END

    使用 BEGIN 和 END 语句可以分别指定在处理实际开始之前或者完成之后进行操作。BEGIN 语句最常用于建立变量或显示标题。另一方面,END 语句可用于在程序结束后继续进行处理。

    在前面的示例中,利用以下例程生成了物品的总价值:


    awk '{x=x+($2*$3)} {print x}' inventory


    该例程在运行总计累加时显示了文件中的每一行。没有其他方法可以指定它,而不让在每一行进行打印也导致它始终不打印出来。但是,利用 END 语句可以避免这一问题:


    $ awk '{x=x+($2*$3)} END {print "Total Value of Inventory:"x}' inventory
    Total Value of Inventory: 2241.66
    $


    定义了变量 x,它对每一行进行处理;但是,在所有处理完成之前不会生成显示。尽管可以作为独立例程使用,它也可以置入到先前的代码列表,添加更多信息并生成更完整的报表:


    $ awk '{x=x+($2*$3)} {print $1,"QTY: "$2,"PRICE:
        "$3,"TOTAL: "$2*$3} END {print "Total Value of Inventory: " x}' inventory

    hammers QTY: 5 PRICE: 7.99 TOTAL: 39.95
    drills QTY: 2 PRICE: 29.99 TOTAL: 59.98
    punches QTY: 7 PRICE: 3.59 TOTAL: 25.13
    drifts QTY: 2 PRICE: 4.09 TOTAL: 8.18
    bits QTY: 55 PRICE: 1.19 TOTAL: 65.45
    saws QTY: 123 PRICE: 14.99 TOTAL: 1843.77
    nails QTY: 800 PRICE: .19 TOTAL: 152
    screws QTY: 80 PRICE: .29 TOTAL: 23.2
    brads QTY: 100 PRICE: .24 TOTAL: 24
    Total Value of Inventory: 2241.66
    $


    BEGIN 命令与 END 的工作方式相同,但它建立了那些需要在完成其他工作之前所做的项目。该过程最常见的目的是创建报表的标题。此例程的语法类似于


    $ awk 'BEGIN {print "ITEM   QUANTITY   PRICE   TOTAL"}'


    输入、输出和源文件

    AWK 工具可以从文件中读取其输入,正如在此之前所有示例所做的那样,它也可以从其他命令的输出中获取输入。例如:


    $ sort emp_names | awk '{print $3,$2}'


    awk 命令的输入是排序操作的输出。除了 sort,还可以使用任何其他的 Linux 命令 - 例如 grep。该过程允许您在离开所选定字段前对文件执行其他操作。

    类 似于解释程序,AWK 使用输出改向操作符 > 和 >> 将其输出放入文件中而不是标准输出设备。这些符号的作用类似于它们在解释程序中的对应符号,因此 > 在不存在文件时创建文件,而 >> 追加到现有文件的尾部。请看以下的示例:


    $ awk '{print NR, $1 ) > "/tmp/filez" }' emp_names
    $ cat /tmp/filez
    1 46012
    2 46013
    3 46015
    4 46017
    5 46018
    6 46019
    7 46021
    8 46022
    9 46024
    10 46026
    11 46027
    12 46029
    $


    检 查该语句的语法,您会看到输出改向是在打印语句完成后进行的。必须将文件名包含在引号中,否则它只是一个未初始化的 AWK 变量,而将指令联接起来会在 AWK 中产生错误。(如果不正确地使用改向符号,则 AWK 无法了解该符号意味着“改向”还是一个关系操作符。)

    在 AWK 中输出到管道也类似于解释程序中所实现的相同操作。要将打印命令的输出发送到管道中,可以在打印命令后附加管道符号以及命令的名称,如下所示:


    $ awk '{ print $2 | "sort" }' emp_names
    BOGUE
    BUCK
    DULANEY
    DURHAM
    FELDMAN
    FERGUS
    JUNE
    KANE
    STEEN
    SWIM
    TUTTLE
    WOOD
    $


    这是输出改向的情况,必须将命令包含在引号中,而管道的名称是被执行命令的名称。

    AWK 所使用的命令可以来自两个地方。首先,可以在命令行中指定它们,如示例中所示。其次,它们可以由源文件提供。如果是这种情况,通过 -f 选项将这种情况向 AWK 发出警告。演示如下:


    $ cat awklist
    {print $3,$2}
    {print $4,$5," "}
    $

    $ awk -f awklist emp_names
    EVAN DULANEY
    MOBILE AL

    JEFF DURHAM
    MOBILE AL

    BILL STEEN
    MOBILE AL

    EVAN FELDMAN
    MOBILE AL

    STEVE SWIM
    UNKNOWN AL

    ROBERT BOGUE
    PHOENIX AZ

    MICAH JUNE
    PHOENIX AZ

    SHERYL KANE
    UNKNOWN AR

    WILLIAM WOOD
    MUNCIE IN

    SARAH FERGUS
    MUNCIE IN

    SARAH BUCK
    MUNCIE IN

    BOB TUTTLE
    MUNCIE IN

    $


    注意,在源文件中的任何地方或者在命令行中调用它时,不使用单引号。单引号只用于区别命令行中的命令与文件名称。

    如果简单的输出不能处理您的程序中所需要的复杂信息,则可以尝试由 printf 命令获得的更加复杂的输出,其语法是


    printf( format, value, value ...)


    该语法类似于 C 语言中的 printf 命令,而格式的规格是相同的。通过插入一项定义如何打印数值的规格,可以定义该格式。格式规格包含一个跟有字母的 %。类似于打印命令,printf 不必包含在圆括号中,但是可以认为使用圆括号是一种良好的习惯。

    下表列出 printf 命令提供的各种规格。

    规格 说明
    %c 打印单个 ASCII 字符
    %d 打印十进制数
    %e 打印数字的科学计数表示
    %f 打印浮点表示
    %g 打印 %e 或 %f;两种方式都更简短
    %o 打印无符号的八进制数
    s 打印 ASCII 字符串
    %x 打印无符号的十六进制数
    %% 打印百分号;不执行转换

    可以在 % 与字符之间提供某些附加的格式化参数。这些参数进一步改进数值的打印方式:

    参数 说明
    - 将字段中的表达式向左对齐
    ,width 根据需要将字段补齐到指定宽度(前导零使用零将字段补齐) 
    .prec 小数点右面数字的最大字符串宽度或最大数量


    printf 命令能够控制并将数值从一种格式转换为另一种格式。当需要打印变量的值时,只需提供一种规格,指示 printf 如何打印信息(通常包含在双引号中)即可。必须为每个传递到 printf 的变量包含一个规格参数;如果包含过少的参数,则 printf 不会打印所有的数值。

    处理错误

    AWK 工具报告所发生错误的方式很令人恼火。一个错误会阻碍任何操作的进行,所提供的错误信息非常含混不清:


    awk: syntax error near line 2
    awk: bailing out near line 2


    您可能会花几小时的时间查看第 2 行,试图找出它为什么阻碍程序运行;这就是支持使用源文件的一个有力论据。 接下来的步骤


    切记有两条规则可以帮助您避免出现语法错误:

    1. 确保命令位于括号中,而括号位于单引号中。没有使用这些字符之一必然导致程序无法运行。

    2. 搜索命令需要位于斜线之间。要找出住在印第安那州的员工,您必须使用“/IN/”而不是“IN”。

    结论

    尽 管 AWK 完全代表另外的含意,但它应该是管理员智能工具包的首字母缩写。连同 SED 一起,AWK 实用工具是 Linux 管理员所拥有的功能最强大和灵活的工具之一。通过了解其语言的一些特性,您可以开辟出能够简化任务的领域,否则这些任务将会是非常费时和困难的


     GAWK

    第一章 前言
    第二章 简介
    第三章 读取输入档案
    第四章 印出
    第五章 Patterns
    第六章 算式(Expression)作为Actions的叙述
    第七章 Actions里面的控制叙述
    第八章 内建函式(Built-in Functions)
    第九章 使用者定义的函式
    第十章 □例
    第十一章 结论


    =======================================

    第一章 前言

    awk 是一个程式语言,对於资料的处理具有很强的功能。对於文
    字档里的资料做修改、比对、抽取等的处理,awk 能够以很短的程式
    轻易地完成。如果使用 C 或 Pascal 等语言写程式完成上述的动作,
    会不方便且很花费时间,所写的程式也会很大。

    awk 能够依照使用者的定义格式来分解输入资料,也可依照使用
    者定义的格式来印出资料。

    awk 名称的由来是由它的原始设计者的姓氏之第一个字母而命名
    :Alfred V. Aho, Peter J. Weinberger, Brian W. Kernighan。
    awk最初在1977年完成。一个新版本的awk在1985年被发表,它的功能
    比旧版本增强不少。

    gawk 是GNU所做的 awk,gawk 最初在1986年完成,之後不断地
    被改进、更新。gawk 包含 awk 的所有功能。

    往後的 gawk 将以下面的2个输入档案来做例子说明。

    档案'BBS-list':
    aardvark 555-5553 1200/300 B
    alpo-net 555-3412 2400/1200/300 A
    barfly 555-7685 1200/300 A
    bites 555-1675 2400/1200/300 A
    camelot 555-0542 300 C
    core 555-2912 1200/300 C
    fooey 555-1234 2400/1200/300 B
    foot 555-6699 1200/300 B
    macfoo 555-6480 1200/300 A
    sdace 555-3430 2400/1200/300 A
    sabafoo 555-2127 1200/300 C

    档案'shipped':
    Jan 13 25 15 115
    Feb 15 32 24 226
    Mar 15 24 34 228
    Apr 31 52 63 420
    May 16 34 29 208
    Jun 31 42 75 492
    Jul 24 34 67 436
    Aug 15 34 47 316
    Sep 13 55 37 277
    Oct 29 54 68 525
    Nov 20 87 82 577
    Dec 17 35 61 401

    Jan 21 36 64 620
    Feb 26 58 80 652
    Mar 24 75 70 495
    Apr 21 70 74 514


    第二章 简介

    gawk 的主要功能是针对档案的每一行(line)搜寻指定的 patterns
    。当一行里有符合指定的 patterns,gawk 就会在此一行执行被指定
    的 actions。 gawk 依此方式处理输入档案的每一行直到输入档案结
    束。

    gawk 程式是由很多的 pattern 与 action 所组成,action 写在
    大括号 { } 里面,一个pattern後面就跟著一个action。整个 gawk 程
    式会像下面的样子:

    pattern {action}
    pattern {action}

    在 gawk 程式里面的规则,pattern 或 action 能够被省略,但
    是两个不能同时被省略。如果 pattern 被省略,对於输入档里面的
    每一行,action 都会被执行。如果 action 被省略,内定的 action
    则会印出所有符合 pattern 的输入行。


    2.1 如何执行gawk程式

    基本上,有2个方法可以执行gawk程式。

    □如果 gawk 程式很短,则 gawk 可以直接写在 command line,如下所示:

    gawk 'program' input-file1 input-file2 ...

    其中 program 包括一些 pattern 和 action。

    □如果 gawk 程式较长,较为方便的做法是将 gawk 程式存在一个档案,
    即 patterns 与 actions 写在档名为 program-file 的档案里面,执行
    gawk 的格式如下所示:

    gawk -f program-file input-file1 input-file2 ...

    gawk 程式的档案不止一个时,执行gawk 的格式如下所示:

    gawk -f program-file1 -f program-file2 ... input-file1
    input-file2 ...


    2.2 一个简单的例子

    现在我们举一个简单的例子,因为 gawk 程式很短,所以将 gawk 程
    式直接写在 command line。

    gawk '/foo/ {print $0}' BBS-list

    实际的 gawk 程式为 /foo/ {print $0}。/foo/ 为 pattern,意思为搜
    寻输入档里的每一行是否含有子字串 'foo',如果含有 'foo' 则执行 action。
    action 为 print $0,是将现在这一行的内容印出。BBS-list 是输入的档案。

    执行完上述指令後,会印出下面的结果:
    fooey 555-1234 2400/1200/300 B
    foot 555-6699 1200/300 B
    macfoo 555-6480 1200/300 A
    sabafoo 555-2127 1200/300 C


    2.3 一个较复杂的例子

    gawk '$1 == "Feb" {sum=$2+$3} END {print sum}' shipped

    现在这个例子会将输入档 'shipped' 的第一个栏位与 "Feb" 做比较
    ,如果相等,则其对应的第2栏位与第3栏位的值会被加到变数 sum。
    对於输入档的每一行重复上述的动作,直到输入档的每一行都被处理
    过为止。最後将 sum 的值印出。END {print sum} 的意思为在所有的输
    入读完之後,执行一次 print sum 的动作,也就是把 sum 的值印出。

    下面是执行的结果:
    84


    第三章 读取输入档案

    gawk的输入可以从标准输入或指定的档案里读取。输入的读取单
    位被称为”记录”(records),gawk 在做处理时,是一个记录一个记
    录地处理。每个记录的内定值是一行(line),一个记录又被分为多个
    栏位(fields)。


    3.1 如何将输入分解成记录(records)

    gawk 语言会把输入分解成记录(record)。记录与记录之间是以
    record separator 隔开,record separator 的内定值是表示新一行的
    字元(newline character),因此内定的 record separator 使得文字
    的每一行是一个记录。

    record separator 随著内建变数 RS 的改变而改变。RS 是一个字串,
    它的内定值是" "。仅有 RS 的第一个字元是有效的,它被当作 record
    separator,而 RS 的其它字元会被忽略。

    内建变数 FNR 会储存目前的输入档案已经被读取的记录之个数。内
    建变数 NR 会储存目前为止所有的输入档案已经被读取的记录之个数。


    3.2 栏位(field)

    gawk 会自动将每个记录分解成多个栏位 (field)。类似於字在一
    行里面,gawk 的内定动作会认为栏位之间是以 whitespace 分开。在
    gawk 里,whitespace 的意思是一个或多个空白或 tabs。

    在 gawk 程式里面,以'$1'表示第一个栏位,'$2'表示第二个栏位
    ,依此类推。举个例子,假设输入的一行如下所示:

    This seems like a pretty nice example.

    第一个栏位或 $1 是'This',第二个栏位或 $2 是 'seems',依此类推。
    有个地方值得特别注意,第七个栏位或 $7 是'example.'而非'example'。

    不论有多少栏位,$NF 可用来表示一个记录的最後一个栏位。以
    上面的例子为例,$NF 与 $7 相同,也就是'example.'。

    NF 是一个内建变数,它的值表示目前这个记录之栏位的个数。

    $0,看起来好像是第零个栏位,它是一个特例,它表示整个记录。

    下面是一个较复杂的例子:

    gawk '$1~/foo/ {print $0}' BBS-list

    结果如下:
    fooey 555-1234 2400/1200/300 B
    foot 555-6699 1200/300 B
    macfoo 555-6480 1200/300 A
    sabafoo 555-2127 1200/300 C

    这个例子是把输入档'BBS-list'的每个记录的第一个栏位作检查,如
    果它含有子字串'foo',则这一个记录会被印出。


    3.3 如何将记录分解成栏位

    gawk 根据 field separator 将一个记录分解成栏位。field sepa-
    rator 以内建变数 FS 表示。

    举个例子,假如 field separator 是'oo',则下面的行:

    moo goo gai pan

    会被分成三个栏位:'m'、' g'、' gai pan'。

    在 gawk 程式里,可以使用'='来改变 FS 的值。例如:

    gawk 'BEGIN {FS=","}; {print $2}'

    输入行如下:

    John Q. Smith, 29 Oak St., Walamazoo, MI 42139

    执行gawk的结果将印出字串 ' 29 Oak St.'。BEGIN 後面的 action 会在
    第一个记录被读取之前执行一次。


    第四章 印出

    在gawk程式里,actions 最常做的事就是印出(printing)。简单
    的印出,使用 printe叙述。复杂格式的印出,使用 printf 叙述。


    4.1 print叙述

    print 叙述用在简单、标准的输出格式。叙述的格式如下所示:

    print item1, item2, ...

    输出时,各个 item 之间会以一个空白分开,最後会换行(newline)。

    如果 'print'叙述之後没有跟著任何东西,它与'print $0'的效
    果一样,它会印出现在的记录(record)。要印出空白行可使用'print
    ""'。 印出一段固定的文字,可用双引号将文字的两边括起来,例如
    'print "Hello there"'。

    这里是一个例子,它会把每个输入记录的前二个栏位印出:

    gawk '{print $1,$2}' shipped

    结果如下所示:
    Jan 13
    Feb 15
    Mar 15
    Apr 31
    May 16
    Jun 31
    Jul 24
    Aug 15
    Sep 13
    Oct 29
    Nov 20
    Dec 17

    Jan 21
    Feb 26
    Mar 24
    Apr 21


    4.2 Output Separators

    前面我们已提过如果 print 叙述包含有多个 item,item 之间
    用逗点分开,则印出时各个item会被一个空白隔开。你能够使用任何
    的字串作为 output field separator,可以经由内建变数 OFS 的设
    定来更改 output field separator。OFS 的初始值为" ",即一格的
    空白。

    整个 print 叙述的输出被称为 output record。print 叙述输
    出 output record 之後,会接著输出一个字串,此字串称为 output
    record separator。内建变数 ORS 用来指明此字串。ORS 的初始值
    为 " ",也就是换行。

    下面这个例子会印出每个记录的第一个栏位和第二个栏位,此二
    个栏位之间以分号';'分开,每行输出之後会加入一个空白行。

    gawk 'BEGIN {OFS=";"; ORS=" "} {print $1, $2}' BBS-list

    结果如下所示:
    aardvark;555-5553

    alpo-net;555-3412

    barfly;555-7685

    bites;555-1675

    camelot;555-0542

    core;555-2912

    fooey;555-1234

    foot;555-6699

    macfoo;555-6480

    sdace;555-3430

    sabafoo;555-2127

    4.3 printf叙述

    printf 叙述会使得输出格式较容易精确地控制。printf 叙述可以
    指定每个 item 印出的宽度,也可以指定数字的各种型式。

    printf 叙述的格式如下:

    printf format, item1, item2, ...

    print 与 printf 的差别是在於 format, printf 的引数比 print
    多了字串 format。format 的型式与 ANSI C 的 printf 之格式相同。

    printf 并不会做自动换行的动作。内建变数 OFS 与 ORS 对 printf 叙
    述没有任何影响。

    格式的指定以字元'%'开始,後面接著格式控制字母。

    格式控制字母如下所示:

    'c' 将数字以 ASCII 字元印出。
    例如'printf "%C",65'会印出字元'A'。

    'd' 印出十进位的整数。

    'i' 印出十进位的整数。

    'e' 将数字以科学符号的形式印出。
    例如

    print "$4.3e",1950

    结果会印出'1.950e+03'。

    'f' 将数字以浮点的形式印出。

    'g' 将数字以科学符号的形式或浮点的形式印出。数字的绝对值如果
    大於等於0.0001则以浮点的形式印出,否则以科学符号的形式印
    出。

    'o' 印出无号的八进位整数。

    's' 印出一个字串。

    'x' 印出无号的十六进位整数。10至15以'a'至'f'表示。

    'X' 印出无号的十六进位整数。10至15以'A'至'F"表示。

    '%' 它并不是真正的格式控制字母,'%%"将印出"%'。

    在 % 与格式控制字母之间可加入 modifier,modifier 是用来进一
    步控制输出的格式。可能的 modifier 如下所示:

    '-' 使用在 width 之前,指明是向左靠齐。如果'-'没有出现,则会在
    被指定的宽度向右靠齐。例如:

    printf "%-4S", "foo"

    会印出'foo '。

    'width' 这一个数字指示相对应的栏位印出时的宽度。例如:

    printf "%4s","foo"

    会印出' foo'。

    width 的值是一个最小宽度而非最大宽度。如果一个 item 的
    值需要的宽度比 width 大,则不受 width 的影响。例如

    printf "%4s","foobar"

    将印出'foobar'。

    '.prec' 此数字指定印出时的精确度。它指定小数点右边的位数。如
    果是要印出一个字串,它指定此字串最多会被印出多少个字
    元。


    第五章 patterns

    在 gawk 程式里面,当 pattern 符合现在的输入记录(record),其
    相对应的 action 才会被执行。


    5.1 Pattern的种类

    这里对 gawk 的各种 pattern 型式作一整理:

    /regular expression/
    一个 regular expression 当作一个 pattern。每当输入记录 (
    record)含有 regular expression 就视为符合。

    expression
    一个单一的 expression。当一个值不为 0 或一个字串不是空的,
    则可视为符合。

    pat1,pat2
    一对的 patterns 以逗号分开,指定记录的□围。

    BEGIN
    END
    这是特别的 pattern, gawk 在开始执行或要结束时会分别执行相
    对应於BEGIN或END的 action。

    null
    这是一个空的pattern,对於每个输入记录皆视为符合pattern。


    5.2 Regular Expressions当作Patterns

    一个 regular expression 可简写为 regexp,是一种描述字串的方
    法。一个 regular expression 以斜线('/')包围当作 gawk 的 pattern。

    如果输入记录含有 regexp 就视为符合。例如:pattern 为 /foo/,
    对於任何输入记录含有'foo'则视为符合。

    下面的例子会将含有'foo'的输入记录之第2个栏位印出。

    gawk '/foo/ {print $2}' BBS-list

    结果如下:
    555-1234
    555-6699
    555-6480
    555-2127

    regexp 也能使用在比较的算式。

    exp ~ /regexp/
    如果 exp 符合 regexp,则结果为真(true)。

    exp !~ /regexp/
    如果 exp 不符合 regexp,则结果为真。


    5.3 比较的算式当作Patterns

    比较的 pattern 用来测试两个数字或字串的关系诸如大於、等於
    、小於。下面列出一些比较的pattern:

    x<y 如果 x 小於 y,则结果为真。
    x<=y 如果 x 小於、等於 y,则结果为真。
    x>y 如果 x 大於 y,则结果为真。
    x>=y 如果 x 大於、等於 y,则结果为真。
    x==y 如果 x 等於 y,则结果为真。
    x!=y 如果 x 不等於 y,则结果为真。
    x~y 如果 x 符合 regular expression y,则结果为真。
    x!~y 如果 x 不符合 regular expression y,则结果为真。

    上面所提到的 x 与 y,如果二者皆是数字则视为数字之间的比较,
    否则它们会被转换成字串且以字串的形式做比较。两个字串的比较,
    会先比较第一个字元,然後比较第二个字元,依此类推,直到有不同
    的地方出现为止。如果两个字串在较短的一个结束之前是相等,则视
    为长的字串比短的字串大。例如 "10" 比 "9" 小,"abc" 比 "abcd" 小。


    5.4 使用布林运算的Patterns

    一个布林(boolean) pattern 是使用布林运算"或"('||'),"及"
    ('&&'),"反"('!')来组合其它的pattern。
    例如:

    gawk '/2400/ && /foo/' BBS-list
    gawk '/2400/ || /foo/' BBS-list
    gawk '! /foo/' BBS-list


    第六章 算式(Expression)作为Actions的叙述

    算式(Expression) 是gawk程式里面action的基本构成者。


    6.1 算术运算

    gawk 里的算术运算如下所示:

    x+y 加
    x-y 减
    -x 负
    +x 正。实际上没有任何影响。
    x*y 乘
    x/y 除
    x%y 求馀数。例如 5%3=2。
    x^y
    x**y x 的 y 次方。例如2^3=8。


    6.2 比较算式与布林算式

    比较算式 (comparison expression) 用来比较字

     
  • 相关阅读:
    poj3984 迷宫问题(简单搜索+记录路径)
    substr
    poj3087 Shuffle'm Up
    学生管理系统
    win10配置gcc编译环境
    poj3278 Catch That Cow
    将字符串str1复制为字符串str2的三种解决方法
    poj2251 Dungeon Master
    cf 410
    7.20 Codeforces Beta Round #8
  • 原文地址:https://www.cnblogs.com/helloWaston/p/4538488.html
Copyright © 2020-2023  润新知