12. 算术运算

可以在模式中进行计算, awk 都将按浮点方式执行运算。支持: + - * / % ^ (幂)


13. 逻辑操作符与复合模式

&& || ,!


6 一个验证 passwd 文件有效性的例子

long@long-Ubuntu:~$ cat /etc/passwd | awk -F: '/

> NF != 7 {/

> printf("line %d, does not have 7 fields:%s/n",NR,$0)}/

> $1 !~ /[A-Za-z0-9]/{printf("line %d, non alpha and numeric user id:%d: %s/n",NR,$0)}/

> $2 == "*" {printf("line %d, no password: %s/n", NR,$0)}'


1 cat 把结果输出给 awk awk 把域之间的分隔符设为冒号。

2 如果域的数量 (NF) 不等于 7 ,就执行下面的程序。

3 printf 打印字符串 "line ?? does not have 7 fields" ,并显示该条记录。

4 若第一个域没包含任何字母和数字, printf 打印“ no alpha and numeric user id" ,并显示记录数和记录。

5 如果第二个域是一个星号,就打印字符串“ no passwd” ,紧跟着显示记录数和记录本身。


7 :几个示例:

$ awk '/^(no|so)/' filename----- 打印所有以模式 no so 开头的行。

$ awk '/^[ns]/{print $1}' filename----- 如果记录以 n s 开头,就打印这个记录。

$ awk '$1 ~/[0-9][0-9]$/(print $1}' filename----- 如果第一个域以两个数字结束就打印这个记录。

$ awk '$1 == 100 || $2 < 50' filename----- 如果第一个或等于 100 或者第二个域小于 50 ,则打印该行。

$ awk '$1 != 10' filename----- 如果第一个域不等于 10 就打印该行。

$ awk '/test/{print $1 + 10}' filename----- 如果记录包含正则表达式 test ,则第一个域加 10 并打印

$ awk '{print ($1 > 5 ? "ok "$1: "error"$1)}' filename—--- 如果第一个域大于 5 则打印问号后面 的表达式值,否则打印冒号后面的表达式值。

$ awk '/^root/,/^mysql/' filename---- 打印从以正则表达式 root 开头的记录到以正则表达式 mysql 开头 的记录范围内的所有记录。如果找到一个新的正则表达式 root 开头的记录,则继续打印直到下一个以正则 表达式 mysql 开头的记录为止,或到文件末尾。


14. 变量

awk 中,变量不需要定义就可以直接使用,变量类型可以是数字或字符串,由 awk 根据上下文推导,不用指定

变量名可以包括字母、数字和下划线,但不能以数字开头。

赋值格式: Variable = expression

变量被设置后,就变成与等号右边那个表达式相同的类型。

未经初始化的变量的值是 0 或者“”,具体是哪个取决于它们被使用时的上下文。

将一个字符串强制转换为数字,方法为: name+0

将数字转换成字符串的方法是: number “”

赋值运算符: = += -= *= /= %= ^=

递增递减运算符,也分为前置和后置两种,遵循的规则与 C 语言中一样


awk 可以在命令行中给变量赋值,然后将这个变量传输给 awk 脚本。如 $ awk -F: -f awkscript month=4 year=2004 filename ,上式的 month year 都是自定义变量,分别被赋值为 4 2004 。在 awk 脚本中,这些变量使用起来就象是在脚本中建立的一样。注意,如果命令行中 filename 的位置在变量之前,那么在 BEGIN 语句中的变量就不能被使用(参见后面的 BEGIN 模式)。

-v 选项, awk -v 选项允许在 BEGIN 语句中,处理命令行变量。从命令行传递的每一个变量前面都必须加 -v 选项


字段变量也可被赋值和修改。新的字段可以通过赋值来创建。字段变量引用的字段如果没有值,则被赋值为空串(即如果只有 4 个字段,但是对 $6 复制,那么不存在的 $5 被赋值为空串)。字段的值发生变化时, awk 会以 OFS 的值作为字段间隔符重新计算 $0 的值。字段数目通常在 100 以内。如 $ awk '{$2 = 100 + $1; print }' test, 上式表示,如果第二个域不存在, awk 将计算表达式 100 $1 的值,并将其赋值给 $2 ,如果第二个域存在,则用表达式的值覆盖 $2 原来的值。


内建变量

$n 当前记录的第 n 个字段,字段间由 FS 分隔。

$0 完整的输入记录。

ARGC 命令行参数的数目。

ARGIND 命令行中当前文件的位置 ( 0 开始算 )

ARGV 包含命令行参数的数组。

CONVFMT 数字转换格式 ( 默认值为 %.6g)

ENVIRON 环境变量关联数组。

ERRNO 最后一个系统错误的描述。

FIELDWIDTHS 字段宽度列表 ( 用空格键分隔 )

FILENAME 当前文件名。

FNR NR ,但相对于当前文件。

FS 字段分隔符 ( 默认是任何空格 )

IGNORECASE 如果为真(即非 0 值),则进行忽略大小写的匹配。

NF 当前记录中的字段数。

NR 当前记录数。

OFMT 数字的输出格式 ( 默认值是 %.6g)

OFS 输出字段分隔符 ( 默认值是一个空格 )

ORS 输出记录分隔符 ( 默认值是一个换行符 )

RLENGTH match 函数所匹配的字符串的长度。

RS 记录分隔符 ( 默认是一个换行符 )

RSTART match 函数所匹配的字符串的第一个位置。

SUBSEP 数组下标分隔符 ( 默认值是 /034)


15. BEGIN 模式

BEGIN 模式后面跟一个操作块。 awk 必须在对输入文件进行任何处理之前,先执行该操作块。常被用于修改内置变量的值,为用户自定义变量赋初值和打印输出的页眉或者标题。。

8

$ awk 'BEGIN{FS=":"; OFS="/t"; ORS="/n/n"}{print $1,$2,$3} filename 。上式表示,在 处理输入 文件以前,域分隔符 (FS) 被设为冒号,输出文件分隔符 (OFS) 被设置为制表符,输出记录分隔符 (ORS) 被设 置为两个换行符。 $ awk 'BEGIN{print "TITLE TEST"} 只打印标题。


编写 awk 脚本时,可以先测试好 BEGIN 块操作,再写程序的其他部分。


16. END 模式

END 模式不匹配任何输入行,而是执行任何与之关联的操作。 Awk 处理完所有输入行之后才处理 END 模式。

9

$ awk 'END{print "The number of records is" NR}' test ,上式将打印所有被处理的记录数。


17. 重定向和管道

awk 可使用 shell 的重定向符进行重定向输出

输入重定向需用到 getline 函数。 getline 从标准输入、管道或者当前正在处理的文件之外的其他输入文件获得输入。它负责从输入获得下一行的内容,并给 NF,NR FNR 等内建变量赋值。如果得到一条记录, getline 函数返回 1 ,如果到达文件的末尾就返回 0 ,如果出现错误,例如打开文件失败,就返回 -1

10

$ awk 'BEGIN{ "date" | getline d; print d}' filename

执行 linux date 命令,并通过管道输出给 getline ,然后再把输出赋值给自定义变量 d ,并打印它。

$ awk 'BEGIN{"date" | getline d; split(d,mon); print mon[2]}' filename

执行 shell date 命令,并通过管道输出给 getline ,然后 getline 从管道中读取并将输入赋值给 d split 数把变量 d 转化成数组 mon ,然后打印数组 mon 的第二个元素。

$ awk 'BEGIN{while( "ls" | getline) print}'

命令 ls 的输出传递给 getline 作为输入,循环使 getline ls 的输出中读取一行,并把它打印到屏幕。这里没有 输入文件,因为 BEGIN 块在打开输入文件前执行,所以可以忽略输入文件。

$ awk 'BEGIN{while (getline < "/etc/passwd" > 0) lc++; print lc}'

awk 将逐行读取文件 /etc/passwd 的内容,在到达文件末尾前,计数器 lc 一直增加,当到末尾时,打印 lc 的值。 注意,如果文件不存在, getline 返回 -1 ,如果到达文件的末尾就返回 0 ,如果读到一行,就返回 1 ,所以命令 while (getline < "/etc/passwd") 在文件不存在的情况下将陷入无限循环,因为返回 -1 表示逻辑真。


如果在 awk 程序中打开了管道 ,就必须先关闭它才能打开另一个管道。管道符右边的命令被括在双引号之间。每次只能打开一个管道。如果打算再次在 awk 程序中使用某个文件或管道进行读写,则可能要先关闭程序,因为其中的管道会保持打开状态直至脚本运行结束。注意:管道一旦被打开,就会保持打开状态直至 awk 退出。 END 块中的语句也会受到管道影响。通过 close() 可关闭管道

11

(脚本)

{ print $1,$2,$3 | “sort -r +1 -2 +0 -1” }

END{

close(“sort -r +1 -2 +0 -1”)

<rest of statements> }


awk 内置函数 system Linux 系统命令作为参数,执行该命令并将命令的退出状态返回给 awk 程序。作为参数的命令必须加双引号。


18. 条件语句

awk 条件语句源于 C 语言,可以用他们对包含判断语句的程序进行控制。

if 语句

格式 : {if (expression){

statement; statement; ...

}

}

12

$ awk '{if ($1 <$2) print $2 "too high"}' test

如果第一个域小于第二个域则打印。

$ awk '{if ($1 < $2) {count++; print "ok"}}' test

如果第一个域小于第二个域,则 count 1 ,并打印 ok


if/else 语句

格式: {if (expression){

statement; statement; ...

}

else{

statement; statement; ...

}

}

13

$ awk '{if ($1 > 100) print $1 "bad" ; else print "ok"}' test

如果 $1 100 则打印 $1 bad, 否则打印 ok

$ awk '{if ($1 > 100){ count++; print $1} else {count--; print $2}' test

如果 $1 大于 100 ,则 count 加一,并打印 $1 ,否则 count 减一,并打印 $1


if/else else if 语句

格式: {if (expression){

statement; statement; ...

}

else if (expression){

statement; statement; ...

}

else if (expression){

statement; statement; ...

}

else {

statement; statement; ...

}

}


19. 循环语句

常常用来对记录中的每个字段重复执行某些操作 ,或者在 END 块中用来循环处理某个数组中的所有元素。三种类型的循环: while 循环, for 循环,特殊 for 循环

while 循环

第一步给一个变量赋初值,在 while 中测试该变量,若值为真(非 0 ),则进入循环执行语句。 do while 循环与 while 类似,唯一的区别是 do while 至少执行一次循环体,然后才测试表达式。

14

$ awk '{ i = 1; while ( i <= NF ) { print NF,$i; i++}}' test

变量的初始值为 1 ,若 i 小于可等于 NF( 记录中域的个数 ), 则执行打印语句,且 i 增加 1 。直到 i 的值大于 NF.


for 循环

for 循环的圆括号中需要 3 个表达式,前两个分别是初始化和测试表达式,第 3 个用于更新测试表达式所用的变量。注意, for 循环中,第一条语句只能初始化一个变量(这与 C 语言不同)

15

$ awk '{for (i = 1; i<NF; i++) print NF,$i}' test 。作用同上。


循环控制

break 语句用于在满足条件的情况下跳出循环

continue 语句用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端。

16

1. {for ( x=3; x<=NF; x++)

if ($x<0){print "Bottomed out!"; break}}

2. {for ( x=3; x<=NF; x++)

if ($x==0){print "Get next item"; continue}}


20. 程序控制语句

next 语句,从输入文件中取出下一行输入,然后从 awk 脚本的顶部重新开始执行。

exit 语句,用于终止 awk 程序。它只能中断对记录的处理,不能跳过 END 语句。若 exit 语句的参数是一个 0~255 之间的值,这个值会被打印在命令行上,以表明程序是否执行成功 ,并指出失败类型。


21. 数组

数组在 awk 中被称为关联数组 associative arrays ),其下表既可以是字符串也可以是数字。数组的键和值都存储在 awk 程序内部的一个表中,该表采用的是 散列算法,所以数组元素不是顺序存储的。数组也是被用到时才被创建。 awk 还能判定数组用于保存数字还是字符串,根据上下文被初始化为 0 或者空字符串。数组大小不需要声明。

用变量作为数组下标。

17

$ awk {name[x++]=$2};END{for(i=0;i<NR;i++) print i,name[i]}' test

数组 name 中的下标是一个自定义变量 x awk 初始化 x 的值为 0 ,在每次使用后增加 1 。第二个域的值被赋给 name 数组的各个元素。在 END 模块中, for 循环被用于循环整个数组,从下标为 0 的元素开始,打印那些存储在数组中的值。因为下标是关健字,所以它不一定从 0 开始,可以从任何值开始。


特殊 for 循环

当下标为字符串或者非连续的数字时,不能用 for 循环来遍历数组。这是就要使用特殊的 for 循环。

格式: for item in arrayname) {

print arrayname[item]

}

}


用字符串作为数组下标

数组下表可以由包含单个字符或字符串的变量组成,如果是字符串,就必须用双引号括起来。


用字段的值作为数组下标

18

long@long-Ubuntu:~$ cat datafile1

4234 Tom 43

4567 Arch 45

2008 Eliza 65

4571 Tom 22

3298 Eliza 21

4622 Tom 53

2345 Mary 24

long@long-Ubuntu:~$ awk '{count[$2]++}END{for(name in count)print name, / count[name]}' dataf ile1

Arch 1

Tom 3

Eliza 2

Mary 1

统计文件中某个字段出现的次数


数组与函数

awk 的内置函数 split 能够将字符串拆分为 词,然后保存在数组中。

格式: split (字符串,数组,字段分隔符)

split (字符串,数组)

awk 的内置函数 delete 用于删除数组元素


多维数组

awk 定义多为数组 的方法是把多个下标串成字符串,下标之间用内置变量 SUBSEP 的值分隔。变量 SUBSEP 的值默认为“ /034” ,这是个不可打印的字符,不太可能用作下标中的字符。

19

long@long-Ubuntu:~$ cat datafile2

1 2 3 4 5

2 3 4 5 6

3 4 5 6 7

6 7 8 9 10

long@long-Ubuntu:~$ awk '{nf=NF

for(x=1; x<=NF; x++){

matrix[NR,x] = $x

}

}END{

for(x=1;x<=NR;x++){

for(y=1;y<=nf;y++)

printf "%d/t", matrix[x,y]

printf "/n"

}

}' datafile2

1 2 3 4 5

2 3 4 5 6

3 4 5 6 7

6 7 8 9 10