AWK


Linux下AWK命令


1. AWK简介

1.1 AWK的功能

AWK被设计为一种方便的文本处理脚本。

1.2 AWK的限制

AWK只被设计为处理文本文件,不支持数据库、socket这样的特性。

AWK不支持用户扩展——和目前流行的TCL、Python这样的脚本不一样,AWK不支持用户扩展。也就是说,你的实现完全依赖于AWK自身所提供的功能,如果AWK没有提供相应的功能,用户自己是没有办法通过对AWK做一些修改来完成相应的功能的。

1.3 应用场所

虽然AWK存在一些限制,但是因为它属于UNIX的标准程序,在任何机器上都会有AWK的某种实现。加之本身的功能也还算比较强大,如果只是想实现文本挑选、转换这样的工作,还是建议使用,这样可以不受机器的限制。

另外,在进行SHELL编程时,常使用AWK来进行一些文字处理,比如,对命令 ps -ef 的结果进行处理,从中获取进程号。

1.4 如何学习

在C语言中,程序所执行的每个功能都是程序员写的,在AWK中,除了程序员所写的代码之外,还有AWK内置的一些机制,所以,要学习AWK编程,首先要了解AWK的工作原理,要知道AWK在代码之外都做了什么,具体可以参见“AWK运行机制”。

在了解AWK的机制之后,就可以沿着“模式、变量、语句、函数”的顺序依次学习。AWK的模式中,主要的内容是正则表达式,如果对正则表达式不太了解,也没有太大关系,只要知道模式的概念,就可以先学习后面的内容,等其它内容学完之后,再回过头来学习。

像特殊变量、内置函数、语句格式这样一些内容,能记住当然是最好不过的。有些记不住也没有太多关系,需要时再查看本文档或系统的man手册。

1.5 程序所在路程

本文所描述的AWK在各个操作系统下,分别位于:

AIX:/usr/bin/awk
HP:/usr/bin/awk
SUN:/usr/xpg4/bin/awk
Linux: /bin/gawk

需要注意,在SUN下,有三个awk程序,/usr/bin/awk、/usr/bin/nawk 和 /usr/xpg4/bin/awk 。本文中所说的awk只 /usr/xpg4/bin/awk 。另外两个awk功能不完备,注意不要使用。

2. AWK运行机制

2.1 AWK机制介绍

一个AWK程序由一个或多个 pattern、action 语句组成,action 写在大括号里面。不同的 pattern、action 语句之间使用换行或分号来分隔。这样,AWK程序大致就形如:

pattern   { action }
pattern   { action }
......
user-function

可以这样理解,pattern 是条件,action是动作。对待处理文件中的每一条记录,AWK会依次判断是否满足各个 pattern,如果满足,则执行对应的 action。一个 pattern、action 处理完后,会接着处理下一个 pattern、action ,直到将 AWK 程序中所有的 pattern、action 对处理完为止,这样才算对一条记录处理完毕。

pattern 或 action 可以被省略,但不能同时被省略:

  • 如果 pattern 被省略,则对于输入文件的每一条记录,action都会被执行。
  • 如果 action (包括大括号)被省略,则系统缺省会输出满足 pattern 的行。相当于action 是 { print $0 }

在action中,可以出现 if 、for 这样的分支、循环语句,所以,AWK具有一定的编程能力。

AWK会根据所指定的记录分隔符(RS),将待处理文件分隔成多个记录(record,通常一个记录就对应于文件中的一行)。再根据指定的字段分隔符(FS,缺省时是space,包括空格和tab),将每一行分隔成多个字段(field)。在AWK中,使用 $1、$2 ……、$n 来访问各个字段,而 $0 则表示整个记录。假设待处理文件内容为:

This is a dog.
That is an apple.

如果使用缺省的RS和FS(分别是换行符和space),则AWK会先读取一行,“This is a dog.”,然后将其分成四个字段“this”、“is”、“a”和“dog.”。

AWK在将一条记录分隔成多个字段后,才会依照pattern在awk文件中出现的顺序依次匹配各个pattern。在一个patten匹配成功后,就会执行相应的action。可以参考下面的“完整的AWK流程”一节。

user-function是用户自定义的函数,可以放在AWK中的任意位置,我的建议是放在文件尾,这样比较好查看:打开一个AWK文件,首先看到的是业务的逻辑;看到最后,才需要涉及到相应的功能函数。

2.2 完整的AWK流程

可以使用下面的伪码来表示完整的AWK处理流程:

for (i = 0; i <启动参数中 -v Assignment 数目; i++)
{
    执行第 i 个 -v Assignment,为相应的变量赋值
}

for (i = 0; i < BEGIN模式的数目; i++)
{
    处理第i个BEGIN模式对应的操作
}

for (i = 0; i < 启动参数中 File 及 Assignment 参数的总个数; i++)
{
    if (第 i 个参数是 Assignment)
    {
        执行对应的Assignment,为相应的变量赋值
        continue;
    }

    打开第i个参数对应的待处理文件
    while ( not 文件结束)
    {
        根据RS,从待处理文件中读取一条记录
        根据FS,将读取的记录分隔成不同的字段
        for (j = 0; j < pattern的数目; j++)
        {
            if (第j个pattern匹配成功) //如果没有指定pattern,则总是匹配成功
            {
                执行对应的action     //如果没有指定action,缺省为 print $0
            }
        }  // 所有pattern匹配结束
    }  // 一个文件处理结束
}  // 所有文件都处理结束

for (i = 0; i < END模式的数目; i++)
{
    处理第i个END模式对应的操作
}

从上面的流程中可以看出,在AWK中,有三种不同优先级别的模式,按照优先级从高到低,依次是:BEGIN模式、普通模式(除了BEGIN和END之外的其它模式)、END模式。同一优先级的多个模式,AWK是执照在AWK文件中出现的先后顺序执行的。不同优先级的模式可以在AWK文件中按照任意顺序放置。

3. AWK启动参数

awk 启动格式为:

awk [ -F Ere ] [ -v Assignment ] ... { -f ProgramFile | 'Program' } [ [ File ... | Assignment ... ] ] ...

注意:启动参数的顺序是不能随便调整的,比如, -v Assignment 就不能放在 File 之后。所以,最保险的做法,就是严格执照上面顺序来写。

3.1 -F Ere

使用扩展正则表达式 Ere 作为字段分隔符(对应于特殊变量 FS)

3.2 -v Assignment

定义可以在AWK语言中访问的标量变量。

Assignment的格式为:Name=Value (注意,Name,=,Value三者之间不能有空格!),其中,Name是变量的名字,可以是任何以字符或下划线开头,后跟字母、下划线和数字的任意组合。Value是变量的值,可以是下划线、数字和字母的任意组合。且前面和后面都有一个”(双引号),如果Value部分是数字,则也将为变量指定对应的数字值。

通过 -v 标志指定的赋值操作发生在 BEGIN 操作之前,所以,可以在 BEGIN 操作中引用。

可以有多个 -v 标志,用来为多个变量赋值。

3.3 -f ProgramFile

从 ProgramFile 变量指定的文件名中获取 AWK 命令的指令。如果多次指定 -f 标志,则文件的串联(按照指定的顺序)将用作指令集。

3.4 'Program'

包含 awk 命令的指令。如果不指定 -f 标志,Program变量应该是命令行上的第一个项,并且应该包括在’’(单引号)中。如:

awk '{print $0}'   x.unl

3.5 Assignment

和 “-v Assignment”的功能相同,只是执行的时间不一样。

Assignment 会在awk程序扫描到 Assignment 才被执行。可以参考“完整的AWK流程”一节。

注意,从AWK的流程中可以看出,BEGIN 操作中不能访问Assignment 所指定的变量!但可以访问 -v Assignment 所指定的变量。

3.6 File

指定包含要处理的输入的文件名称。如果不指定 File 变量,或指定了 - (减号),则处理标准输入。

4. AWK返回值

AWK命令返回值为:

返回值 含义
0 表示成功执行完毕
>0 发生错误

如果是在 SHELL 编程中执行awk命令,则通常需要判断awk命令的返回值。

注意:可以通过在awk命令中使用 exit [Expression] 语句来更改程序的退出状态。

5. AWK模式

模式可以有以下几种类型:

5.1 正则表达式

awk所使用的扩展正则表达式类似于 grep 和 egrep 。当记录满足正则表达式的内容时,就认为模式匹配。

一个 regular expression 以斜线(‘/’)包围当作 AWK 的 pattern。如果输入记录含有 regexp 就视为符合。例如:pattern 为 /foo/,对於任何输入记录含有’foo’则视为符合。

awk '/foo/' input.txt

regexp 也能使用在比较的算式:
exp ~ /regexp/ :如果 exp 符合 regexp,则结果为真(true)。
exp !~ /regexp/ :如果 exp 不符合 regexp,则结果为真。

awk -F"|" '$2 ~ /abc/' f.unl	# 打印出第2个字段包含 abc 的记录

假定一个名为 testfile 的文件具有以下内容:

smawley, andy
smiley, allen
smith, alan
smithern, harry
smithhern, anne
smitters, alexis

格式约定:以“>>”作为命令行的提示符,

>> awk '/sm[a-h]/' testfile
smawley, andy

表示执行命令 awk '/sm[^a-h]/' testfile,输出的结果为 smawley, andy 。

在正则表达式中,可以的特殊字符有:

5.1.1. + (加号,plus sign)

+表示重复前面的规则一次或多次。

>> awk '/smith+ern/' testfile
smithern, harry
smithhern, anne

将包含smit ,且后跟一个或多个 h ,再以 ern 结尾的字符串的记录打印至标准输出。

5.1.2. ? (问号,question mark)

?表示重复前面的规则零次或一次。

>> awk '/smith?/' testfile
smith, alan
smithern, harry
smithhern, anne
smitters, alexis

将包含smit ,后有零个或一个 h 字符的记录打印至标准输出。

5.1.3. | (垂直线,vertical bar)

垂直线表示多个规则中只要有一个满足就可以。

>> awk '/allen|alan/' testfile
smiley, allen
smith, alan

将包含字符串 allen 或 alan 的所有记录打印至标准输出。

5.1.4. ( ) (小括号)

( ) 用于将字符串组合在一起。

>> awk '/a(ll)?(nn)?e/' testfile
smiley, allen
smithhern, anne

将具有字符串 ae 或 alle 或 anne 或allnne 的所有记录打印至标准输出。

5.1.5. {m}

如果正好有m个模式的具体值位于字符串中,则字符串匹配。

>> awk '/l{2}/' testfile
smiley, allen

将有2个l 的字符串打印至标准输出。

注:如果有多于2个l,比如, alllen,也算是匹配。为了避免这种情况,通常应该在{m}前、后再多指定一些模式。如:/al{2}en/,这样就只可能匹配 allen 。/al{2}en/是指在模式a和en中间含有且只有两个模式’l’,例如如果存在’acllben’和’alllen’用上述方法就匹配不出来。下面的 {m,} 和 {m,n} 也有类似的情况。

5.1.6. {m,}

如果至少有m个模式的具体值位于字符串中,则字符串匹配。如,

>> awk '/t{2,}/' testfile
smitters, alexis

将至少有两个连续的 t 出现的记录打印至标准输出。

5.1.7. {m,n}

如果有m 和 n 之间(包括 m 和 n)个模式的具体值位于字符串中,则字符串匹配。

>> awk '/er{1,2}/' testfile
smithern, harry
smithhern, anne
smitters, alexis

注:m、,(逗号)和n之间,不能有空格出现!

5.1.8. [String]

指定正则表达式与方括号内 String 变量指定的任何字符匹配。如:

>> awk '/sm[a-h]/' testfile
smawley, andy

将具有 sm 后跟以字母 a 到 h 之间的任何字符的所有记录打印至标准输出。

如果是字母或数字存在连续的情况,则可以使用“起始字母-终止字母”的简写方式,如:[x-z] 表示从x到z的所有字母。如果是不连续字母/数值,则直接写出所有的单个字母,如[axz]表示a、x、z这三个字母。

^[ -_:0-9a-zA-Z]+$

表示由

5.1.9. [^ String]

在 [ ] (方括号)和在指定字符串开头的 ^ (插入记号)指明正则表达式与方括号内的任何字符不匹配。

>> awk '/sm[^a-h]/' testfile
smiley, allen
smith, alan
smithern, harry
smithhern, anne
smitters, alexis

5.1.10. ~~ 和 !~

表示指定变量与正则表达式匹配(代字号)或不匹配(感叹号、代字号)的条件语句。

>> awk '$1 ~ /n/' testfile
smithern, harry
smithhern, anne

将第一个字段包含字符 n 的所有记录打印至标准输出。

5.1.11. ^(circumflex)

指定字段或记录的开头。

>> awk '$2 ~ /^h/' testfile
smithern, harry

把字符 h 作为第二个字段的第一个字符的所有记录打印至标准输出。

5.1.12. $ (dollar sign)

指定字段或记录的末尾。

>> awk '$2 ~ /y$/' testfile
smawley, andy
smithern, harry

把字符 y 作为第二个字段的最后一个字符的所有记录打印至标准输出。

5.1.13. . (句号,period)

表示除了在空白末尾的终端换行字符以外的任何字符。

>> awk '/a..e/' testfile
smawley, andy
smiley, allen
smithhern, anne

将具有两个字符隔开 a 和 e 所有记录打印至标准输出。

5.1.14. * (星号,asterisk)

表示重复前面的规则零次或多次。

>> awk '/a.*e/' testfile
smawley, andy
smiley, allen
smithhern, anne
smitters, alexis

将具有零个或多个字符隔开 a 和 e 所有记录打印至标准输出。

5.1.15. \ (反斜杠,backslash)

转义字符。当位于扩展正则表达式中具有特殊含义的任何字符之前时,转义字符除去该字符的任何特殊含义。如,

/a\/\//

将与模式 a// 匹配,因为反斜杠否定了斜杠作为正则表达式定界符的通常含义。要将反斜杠本身指定为字符,则使用两个反斜杠。

5.1.16. 识别的转义序列:

awk命令识别大多数C语言中约定的转义序列,以及awk命令本身用作特殊字符的几个转义序列。如下表:

转义序列表示的字符
\" "(双引号)字符
\/ /(斜杠)字符
\ddd 其编码由1、2或3位八进制整数表示的字符,其中,d 表示一个八进制数位
\\ \(反斜杠)字符
\a 警告字符
\b 退格字符
\f 换页字符
\n 换行字符
\r 回车字符
\t 跳格字符
\v 垂直跳格字符

注:除了在 gsub、match、split 和 sub 内置函数中,扩展正则表达式的匹配都基于输入记录。记录分隔符(缺省情况下为换行字符)不能嵌套在表达式中,且没有与记录分隔符字符匹配的表达式(因为awk是先对记录、字段作了分隔,才进行匹配操作的)。如果记录分隔符不是换行字符,则可与换行字符匹配。在指定的四个内置函数中,匹配基于文本字符串,且任何字符(包含记录分隔符)可以嵌套在模式中,这样模式与适当的字符相匹配。然而,用awk命令进行 的所有正则表达式匹配中,在模式使用一个或多个NULL(空)字符将生成未定义的结果。

5.2 关系表达式

关系运算符 <(小于)、>(大于)、<=(小于或等于)、>=(大于或等于)、==(等于) 和 != (不等于)可以用来形成模式。例如,模式:

$1 < $4

将与第一个字段 小于第四个字段的记录匹配。

关系运算符还可以和字符串值一起使用,例如:

$1 != "q"

将与第一个字段不是q的所有记录匹配。

字符串值还可以根据校对值匹配,如:

$1 <= "d"

将与第一个字段以字符 a、b、c 或 d 开头的所有记录匹配。如果未给出其它信息,则字段变量作为字符串值进行比较。

5.3 模式的组合

可以使用三种选项组合模式:

  • 范围由两种以 , (逗号)隔开的模式指定。 操作在每个以匹配第一个模式的记录开始的每个记录上执行,并通过匹配第二个模式的记录(包含此记录)继续。例如:
/begin/,/end/

与包含字符串 begin 的记录以及该记录和包含字符串 end 之间的所有记录(包括字符串 end 的记录)匹配。

  • 括号() 将模式组合在一起。
  • 布尔运算符 || (或)、&&(和)以及 ! (不)将模式组合成如果它们求值为真则匹配,否则不匹配的表达式。例如,模式:
$1 == "al" && $2 == "123"

与第一个字段是 a1 且第二个字段是 123 的记录匹配。

5.4 BEGIN 和 END 模式

  • 用BEGIN模式指定的操作在读取任何输入之前执行。
  • 用END指定的操作在读取了所有输入后执行。
  • 允许多个BEGIN和END模式,并以指定的顺序处理它们。在程序语句中END模式可以在BEGIN模式之前。
  • 如果程序仅由BEGIN语句构成,则执行操作且不读取输入。
  • 如果程序仅由END语句构成,则在任何操作执行前读取所有输入。

6. AWK变量

AWK中的变量可以是标量(scalars)、数组(array elements)、字段变量(fields)和特殊变量。变量名称不能以数字开始。

AWK中的变量不需要在使用前先进行申明,变量可以具有数字值和/或字符串值,并根据上下文的不同而呈现出数字或字符串。

未初始化的标量变量和数组元素具有一个为0的数字值和一个为空串(””)的字符串值。

6.1. 特殊变量

AWK 自定义的特殊变量:

变量名含义
ARGC 输入参数的个数,等同于C语言中的 argc,此值可以改变
ARGV 输入参数数组,等同于C语言中的 argv
CONVFMT AWK内部运算时,将数字转换成字符串的 printf 格式。缺省值为”%.6g” 。
ENVIRON 表示AWK运行环境的数组。使用 ENVIRON[“环境变量名”] 来访问某一环境变量的值。如: editor = ENVIRON[“EDITOR”] 。如果访问了不存在的环境变量,则返回一个空串。
FILENAME AWK 当前处理的文件名
FNR 当前处理文件的当前记录数
FS 输入文件的字段分隔符,可以为单个字符,也可以是一个字符串。缺省值是(任意数目的)space (空格或跳格)。FS可以被指定为:\\1、单个字符:此时,字段由该字符的每个具体值隔开。如,指定为字符 “~|”,则每记录中每个 ~| 都被认为是分隔符。两个相邻的 ~| ,被认为是一个空字段。\\2、一个扩展正则表达式:字段由与扩展正则表达式匹配的每个序列的具体值隔开。这样,缺省的FS,可以被理解为: FS = “( ~|\t)+” \\除了在脚本中显示的指定外,还可以通过AWK的启动参数 -Ffs 来指定FS为 fs 所表示的正则表达式。
NF 当前记录的字段个数,最大值为99 。
NR 已读出的记录数,如果只是处理一个文件,则NR = FNR ,如果处理多个文件, NR >= FNR 。在BEGIN操作中,NR的值为0,在END操作中,NR的值为最后处理的记录的值。
OFMT 在输出语句中将数字转换为字符串的printf格式。缺省值为”%.6g” 。
OFS 输出文件的字段分隔符,可以为单个字符,也可以是一个字符串,缺省为空格
ORS 输出文件的记录分隔符,缺省为换行(\n)
RLENGTH 由match函数来匹配的字符串的长度。(参见 match 函数的描述)
RS 输入文件的记录分隔符,缺省为换行(\n) (RS只有第一字符是是有效的,其它字符会被忽略)
RSTART 由 match 函数所匹配的字符串的起始位置,从1 开始编号。变量的值总是和 match 函数的返回值相同。(参见 match 函数的描述)
SUBSEP 多维数组中的下标分隔符。缺省值是"\034"。(详见“数组变量”的介绍)

6.2. 标量变量

在AWK中,变量不需要先申明,直接对其赋值,然后就使用。如:

{
   abc = 1;
   print abc
}

将数字 1 赋值给变量 abc。

6.3. 数组变量

6.3.1. 概念介绍

数组初始为空且大小可以动态改变(类似于C++中的vector)。数组由一个变量名和在[](方括号)中的下标来标示。下标可以是数字,也可以是字符串。

数组可以用一个以上的下标来建立索引,以实现多维数组。AWK中数组实际上是一维的,通过串联各个独立表达式的字符串值(各字符串由特殊变量 SUBSEP 的值隔开)来将逗号隔开的下标转换成为单个字符串上,所以,下面两个索引操作是等同的:

x[expr1,expr2,...exprn]

x[expr1SUBSEPexpr2SUBSEP...exprn]

当使用 in 运算符时,一个多维 Index 值应包含在圆括号之中,除了 in 运算符,任何对不存在的数组元素的引用将自动创建该元素。

6.3.2. 运用举例

赋值:

person[person_num, "name"] = "Jack"
person[person_num, "age"] = "11"
person_num++

上面的代码中,对一个二维数组“person”进行赋值。

6.4. 字段变量

当AWK将一条记录分隔成多个字段后,可以通过 $1、$2 …… 来访问每一个字段($1 表示第一个字段,依次类推)。$0 则表示整个记录。

将一个值指定给不存在的字段(即任何大于$NF字段变量的当前值的字段)将会导致:

  • 创建新的字段变量
  • 修改NF的值
  • 修改$0的值
假设当前输入记录为“1|2|3|4|”,FS和OFS都为“|”,这时,字段数为4(NF为4),如果执行了 $10 = “abc”,则会:创建$5、$6、$7、$8、$9、$10这几个变量,其中,除了 $10 为指定的值”abc”以外,其它变量($5 到 $9)都是空串。NF被AWK修改为10,$0也被修改为从$1到$10对应的值,为1|2|3|4||||||abc

7. 操作符

7.1. 算术操作符

AWK中支持算术操作符: +(加号)、-(减号),/(除号),^(幂),*(乘号),%(求余)。格式为:

Expression  op  Expression

7.2. 一元操作符

AWK支持的一元操作符:+ (正号)、-(负号)、++(递增)、--(递减)。

7.3. 赋值操作符

AWK支持的赋值操作符有:=、+=、-=、*=、/=、%=,格式为

Variable  op  Expression

8. AWK操作

操作在 { } (大括号)中,在括号里可以指定多个操作,但操作间必须以换行字符或分号分隔,且语句以它们出现的顺序处理。操作语句包含:

8.1 条件语句

在AWK中的条件语句的C中的条件语句具有相同的语法和功能。所有语句允许使用 { } (大括号)将语句组合在一起。可以在条件语句的表达式部分和语句部分之间使用可选的换行字符,且换行字符或分号(;)用于隔离{}(大括号)中的多个语句。

8.1.1 遵从C语言规则的条件语句

AWK中共有六种遵从C语言规则的条件语句,分别是:

8.1.1.1 if 语句

if ( Expression ) { Statement } [ else Action ]

如果 Expression 计算结果为真(True),则执行 Statement ,否则,执行 Action (如果有 else 分支的话)。

8.1.1.2 while 语句

while ( Expression ) { Statement }

如果 Expression 的计算结果为真,则执行Statement,然后再次计算 Expression,如果结果为真,则再次执行 Statement,重复这一过程,直到 Expression 的计算结果为假为止。

如果 Expression 的第一次计算结果就为假,则 Statement 永远不会被执行。

8.1.1.3 do - while 语句

do { Statement } while (Expression)

执行 Statement,然后计算 Expression,如果结果为真,则再次执行 Statement,计算 Expression,重复这一过程,直到 Expression 的计算结果为假为止。

如果 Expression 的第一次计算结果就为假,则 Statement 只会被执行一次。

8.1.1.4 for 语句

for ( Expression1 ; Expression2 ; Expression3 ) { Statement }

计算 Expression1,计算 Expression2,如果Expression2计算结果为真,则执行 Statement,然后再执行 Expression3 。重复这一过程(Expression2、Statement、Expression3)直到 Expression2 的计算结果为假为止。

如果 Expression2 的第一次计算结果就为假,则 Statement 和 Expression3 永远不会被执行。

8.1.1.5 break 语句

当break 语句用于 while 或 for 语句时,导致退出循环

8.1.1.6 continue 语句

当 continue 语句用于while 或 for 语句时,导致循环移动到下一个迭代。

上面只对六种语句作了简单介绍,更多内容可以参考C语言的书。

8.1.2 不遵从C语言规则的条件语句

AWK中共有五种不遵从C语言规则的条件语句,分别是:

8.1.2.1 for … in 语句

for ( Variable in Array ) { Statement }

for … in 语句将 Variable 参数设置为 Array 数组变量的每个索引值,一次一个索引且没有特定的顺序,并用第个迭代来执行 Statement 所指定的操作。请参阅 delete 语句以获得 for … in 语句的示例。

8.1.2.2 delete 语句

delete Array [expression]

delete 语句删除数组Array中指定的数组元素和 Expression 参数指定的索引。例如:语句

for ( i in g)
    delete g[i]

将删除 g[] 数组的每个元素。

8.1.2.3 exit 语句

exit  [Expression]

exit 语句首先(按照在AWK文件中出现的先后顺序)调用所有 END 模式的操作,然后以 Expression 参数指定的退出状态终止AWK命令。如果 exit 语句在 END 操作中出现,则不调用后继的 END 操作。

8.1.2.4 next 语句

停止对当前输入记录的处理,从下一个输入记录继续处理。

8.1.2.5 # 语句

在AWK中,注释以 # 开始,一直持续到行结束,类似于C++中的 // 注释。

8.2 输出语句

AWK中提供了两种输出语句:

8.2.1. print

print 的语法格式为:

print [ExpressionList]  [Redirection]  [Expression]

print 语句将 ExpressionList 参数指定的每个表达式的值写至标准输出,每个表达式由 OFS 特殊变量的当前值隔开,且每个记录由 ORS 特殊变量的当前值终止。

可以使用 Redirection 参数重定向输出,此参数可以使用 > (大于号)、>>(双大于号)和|(管道)进行三种输出重定向。Redirection 参数指定如何重定向输出。

当 Redirection 参数是 > 或 >> 时,Expression 参数是文件的路径名称,如果文件不存在,AWK会自动创建相应的文件。> 表示在写文件时,先清空文件的内容, >> 则表示将内容追加在文件后面。

当 Redirection 参数是 ~| 时,Expression 参数是命令的名称,ExpressionList 的结果将作为命令的输入参数。

注:如果 Expression 参数为 Redirection 参数指定一个路径名称,则 Expression 参数将括在双引号中以确保将它当作字符串对待。

8.2.2. printf

printf 的语法格式为:

printf  Format [ExpressionList]  [Redirection]  [Expression]

printf 语句将 ExpressionList 参数指定的每个表达式的值以 Format 参数指定的格式写至标准输出。 Redirection 和 Expression 参数的作用与 print 语句中相同。

printf 语句中 Format 参数可以使用以下的修饰符(和C语言中的 printf 语句差不多):

修改符含意
%c ASCII字符
%d 整数
%e 浮点数,科学记数法
%f 浮点数,如:123.44
%g awk决定使用哪种浮点数转换 e或 f
%o 八进制数
%x (小写的x) 十六进制数(A-F部分为小写字母)
%X(大写的x) 十六进制数(A-F部分为大写字母)

9. 内置函数

9.1 算术函数

以下算术函数和C语言中的同名函数操作相同。

函数原型函数说明
atan2(y, x) 返回 y/x 的反正切
cos(x) 返回 x 的余弦,x是弧度
exp(x) 返回 x 的幂,e^x
int(x) 返回 x 的截断至整数的值
log(x) 返回 x 的自然对数
rand() 返回0 到1 之间任意数字 n, 其中 0 <= n < 1
sin(x) 返回 x 的正弦,x是弧度
sqrt(x) 返回 x 的平方根
srand([Expr]) 将 rand 函数的种子值设置为Expr参数的值,如果省略Expr参数,则使用系统的当前时间。

9.2 字符串处理函数

AWK内部提供了一系列处理字符串函数。

函数原型函数说明
gsub(ere, repl [, in]) 和sub函数一样,只是会将 in 中所有匹配 ere 的子串都替换为 repl
index(s, t) 返回s中字符串 t 第一次出现的位置,0表示字符串 s 中没有子串 t 。
length[(s)] 返回s长度,如果没有参数,则返回 $0 的长度。
match(s, ere) 返回s中第一个匹配扩展正则表达式 ere 的子串的位置。0表示 s 中没有与 ere 匹配的子串。
匹配成功后,特殊变量 RSTART 和 RLENGTH 被设置为匹配成功的子串的位置和长度。
如果没有匹配成功,则设置为 -1 。
split(s, a[, fs]) 以正则表达式 fs 为分隔符,将字符串 s 分隔成字符串数组 a[1]、a[2]、……、a[n]中,并返回 n 的值。
如果没有指定 fs,则使用特殊变量 FS 的值。
sprintf(fmt, expr, ...) 返回经fmt格式化后的expr的字符串
sub(ere, repl [, in]) 将 in 中的第一个匹配扩展正则表达式 ere 的子串替换为 repl 所指定的串。如果 in 没有指定,则使用 $0 。
substr(s, m [, n]) 返回s中,从 m 开始,最大长度为 n 的子串。
如果没有指定n,则返回从m到字符串s结束的子串。
如果 m+n的值大于 s 的长度,则也返回从m到字符串s结束的子串。
tolower(s) 将s中的字符转换成小写字母后返回。(不修改串s的值)
toupper(s) 将s中的字符转换成大写字母后返回。(不修改串s的值)

说明:

1、在AWK中,字符是从 1 开始计数的。所以,index(“01234”, “0”)的返回结果为1。

>> awk 'BEGIN {print index("01234", "0")}' one.unl
1

2、可以直接将字符串值放在一起来实现字符串的连接(类似于C中的strcat函数)。如果想将多个字符串变量连接在一起,记得要使用括号将变量名括起来,这样AWK才能够知道变量的真实名字。如:

first_name = " Benjamin"
last_name  = " Frnaklin."
hello1 = "this is"first_name
hello2 = "this is"(first_name)(last_name)   # 一定要加括号!

3、在 AWK 中,没有显示的字符串和数字的转换。如果想要将某一个变量 abc 当做数字来进行比较,则应该先使用

abc = abc + 0

来显式地将其转换为数字。

注:在SUN下,如果分隔后的某一个字段中包括了 space,则想直接将其当成数字来和别的数字做比较,会不成功(AWK不会报错,但比较的结果不是想要的结果)。

9.3 其它函数

AWK中的其它函数有:

函数原型函数说明
close(Expression) 用同一个带字符串值 的 Expression 参数来关闭由 print 或 printf 语句打开的或调用 getline 函数打开的文件或管道。如果文件或管道成功关闭,则返回0,其它情况,则返回非零值。如果打算写一个文件,并稍后在一个程序中读取该文件,则必须调用 close 。
system(cmd) 执行命令 cmd 并返回其退出状态。
Expression ~ getline [ Variable ] | 从来自 Expression 参数指定的命令的输出中通过管道传送的流中读取一个输入记录,并将该记录的值指定给 Variable 参数指定的变量。
如果当前未打开将 Expression 参数的值作为其命令名称的流,则创建流。创建的流等同于调用 popen 子例程。此时 Command 参数取 Expression 参数的值且 Mode 参数设置为一个是 r 的值。
只要流保留保留打开且 Expression 参数求得同一个字符串,则对 getline 函数的每次后续调用读取另一个记录。如果未指定 Variable 参数,则$0记录变量和NF特殊变量设置为从流读取的记录。
getline [Variable] < Expression 从Expression参数指定的文件读取输入的下一个记录,并将Variable参数指定的变量设置为该记录的值。只要流保持打开且Expression参数对同一字符串求值,则对getline函数的每次后续调用将读取另一个记录。如果未指定Variable参数,则$0记录变量和NF特殊变量设置为从流读取的记录。
getline [Variable] 将Variable参数指定的变量设置为从当前输入文件读取的下一个输入记录。如果未指定Variable参数,则$0记录变量设置为该记录的值,还将设置NF、NR和FNR特殊变量。

注:所有getline函数的格式对于成功输入返回1,对于文件结束返回0,对于错误返回 -1 。

10. 用户自定义函数

用户定义函数以下面的格式说明:

function Name (Parameter, Parameter,...) { Statements }

函数定义可以位于AWK程序中的任何位置,通常是建议放在文件尾(可以参考“AWK运行机制” 一节),甚至可以先使用后定义。函数的作用域是全局的。

函数的参数可以是标题或数组,参数名称对函数而言是本地的,所有其它变量名称都是全局的,同一个名称不应用作不同的实体;例如:一个参数名称不能用作函数名称又用作特殊变量,具有全局作用域的变量不应共享一个函数的名称,同个作用域中的标题和数组不应具有同一个名称。如果参数是标量,则调用时参数是以传值的方式,如果参数是数组,则调用时使用的是以传引用的方式。

函数定义中的参数数量不必和调用函数时使用的参数数量匹配,多余的形式参数可以用作本地变量,额外的标量参数初始化为空字符串(也可以理解为数字值为0(零))。额外的数组参数初始化为空数组。

当调用函数时,函数名称和左括号之间没有空格。函数调用可以是嵌套的或循环的,从任何嵌套的或循环函数调用返回时,所有调用函数的参数的值保持不变,除了引用传送的数组参数,return 语句可用于返回一个值。

在函数定义内,在左大括号({)之前和右大括号(})之后的换行字符是可选的。

下面是一个函数的例子:

function average (g, n)
{
    for (i in g)
        sum = sum + g[i]
    avg = sum / n
    return avg
}

将数组 g 和变量 n 以及数组中的元素个数传递给函数 average,然后函数获得一个平均值并返回它。

补充说明

这篇文章是03年某个时候写的,主要是参考AWK的手册及几本Linux书籍整理汇总而成。在工作是仍然用得上,权当AWK便捷手册使用,所以将它收录进来。


声明: 本文采用 CC BY-NC-SA 3.0 协议进行授权,转载请注明出处。