Awk

Awk是一种用于处理数据和生成报告的UNIX编程语言。

Awk已逐行方式扫描文件(或输入), 从第一行到最后一行,以查找匹配某个特定模式 的文本行,并对这些文本行执行(括在花括号)中的特定动作。

如果只给出模式而未给出动作,则打印所有匹配行; 如果只给出动作而未指定模式,会对所有输入行执行指定动作。

基本用法

规则

一个规则包含一个模式,后跟一个操作。操作由花括号括起来以与模式分开。通常,换行符用来分开多个规则。

pattern { action }
pattern { action }
...

特别地,, { ? : || && do else 这些符号和关键字后的换行符被忽略,其它时候都被当作一个规则的结束。

多个规则也可以写在同一行,用 ; 分隔。

pattern1 { action1 } ; pattern2 { action2 }

模式

如果某个表达式中没有出现 if,但实际却暗含 if 这个意思,那么,这个表达式就是“模式”。

模式不能被包括在 { } 中,模式包括:

  • 正则表达式

    /regular expression/

  • 表达式

    当表达式的值为非零或非空时匹配;否则不匹配

  • 范围 pat1, pat2

    一对用逗号分开的两个模式,用来指定一个连续范围的记录

  • BEGIN / END

  • BEGINFILE / ENDFILE

  • 匹配每一条记录。

操作

操作由括在 { } 内的一条或多条语句组成,同一行内的多条语句之间用分号 ; 隔开,独占一行的语句则以换行符分隔。

读取输入

缺省字段分隔符

缺省的字段分隔符是 FS = " ",但 awk 对其特殊处理,将它扩展为空白序列,即包括空格,制表符和换行符。

如果仅仅按照字面意思,那么 " " 会将连续的两个空格分成两个独立的字段。

也可以用正则表达式作为字段分隔符,比如 FS = " "FS = "[ \t\n]+" 大体相当,不过也有一些不同。前者时,awk 会剥落记录前导和后缀的空白字符,然后再分出字段;后者会将记录的前导空白字符作为第一个字段,后缀的空白字符作最后一个字段。

$ echo '  a b c d' | awk '{ print $2 }'
b

$ echo '  a b c d' | awk 'BEGIN { FS = "[ \t\n]+" } { print $2 }'
a

使用多个字段分隔符

如果有多个字符被用于字段分隔符FS,则FS对应是一个正则表达式,并且被包含在括号里:

$ awk -F '[ :\t]' '{print $1, $1}' file

改变字段值

一个字段值被改变,该记录也将马上重新计算以反映这个改变。也就是说,$0 包含的是字段值改变过的新记录。

$ awk '{ $2 = $2 - 10; print $0 }' inventory-shipped
Jan 3 25 15 115
Feb 5 32 24 226
Mar 5 24 34 228
...

使用超出范围的字段索引

仅仅索引一个超出范围的字段不会改变记录值,它只会返回一个空字符串。

单如果对一个超出范围的字段赋值,那么将会创建这个字段,NF 更新为这个最大索引值,$0 也将重建。如果这个时候打印 $0,则会输出这个新字段,并且在该字段和原来的旧字段之间填入适当数量的 OFS 符号。

$ echo a b c d | awk '{ OFS = ":"; $2 = ""; $6 = "new"
>					    print $0; print NF }'
a::c:d::new
6

手动重建记录值

改变了字段值,$0 会自动重建;重新赋值 $0,字段也会重新划分。可是仅仅通过改变 OFS(即使在 BEGIN 里)并不会改变 $0 的值。要记住,$0 此时是一个由输入读取的完整的记录,包括前导和后置空格等,而 OFS 是用来输出字段的。

$ echo a b c d | awk 'BEGIN{ OFS = ":" } { print $0 }'
a b c d

可以通过使用看似无用的 $1 = $1 语句来重建 $0

$ echo a b c d | awk 'BEGIN{ OFS = ":" } { $1 = $1; print $0 }'
a:b:c:d

所谓重建 $0 是指,将 $1 直到 $NF 连接起来,中间用 OFS 分隔。下面的例子清晰地标明了 $0 一开始和重建后的不同:

$ echo "  a b c  d" | awk '{ print $0; $2 = $2; print $0 }'
  a b c  d
a b c d

空字符为分隔符

在 Gawk 里,如果设置 FS = "",便可将每个字符分割成一个字段。

传统的 awk 此时会将整个记录当成一个字段。

如果将 RS 设置为 "",这意味这将一行或者连续多行空行当作记录分隔符,没有其它更多所指。

打印输出

OFS 和 ORS

ORS 控制记录的输出;OFS 控制字段的输出。

$ awk 'BEGIN { OFS = ";"; ORS = "\n\n" }
>			 { print $1, $1 }' mail-list
Amelia:555-5553

Anthony:555-3412

Becky:555-7685

...